Mercurial > hgweb.cgi > hwm
changeset 54:5ef160e59286
Reworked the manager functions to use exceptions instead of error codes.
author | John Schneiderman |
---|---|
date | Mon, 09 Mar 2015 21:24:51 +0100 |
parents | 8ef24a759bf6 |
children | 3a0242e260ab |
files | INSTALL doc/ChangeLog doc/TODO setup.cfg src/__init__.py src/_app_info.py src/config.py src/hwm.py src/ignorepo.py src/manager.py src/manrepo.py |
diffstat | 11 files changed, 200 insertions(+), 346 deletions(-) [+] |
line wrap: on
line diff
--- a/INSTALL Mon Mar 09 20:42:19 2015 +0100 +++ b/INSTALL Mon Mar 09 21:24:51 2015 +0100 @@ -29,4 +29,5 @@ Known Issues ============ -None known. +1) When the web configuration file contains the character ':', it's interpreted + as a separator, e.g. http://web-site/repoName.
--- a/doc/ChangeLog Mon Mar 09 20:42:19 2015 +0100 +++ b/doc/ChangeLog Mon Mar 09 21:24:51 2015 +0100 @@ -19,6 +19,7 @@ ******************************************************************************** 2015-00-00 John Schneiderman <Licensing _AT_ CodeGNU _DOT_ com> 0.4.0 - No longer dependant upon third-party general file locker for repositories. +- Manager functions use exceptions for error handling. 2014-08-21 John Schneiderman <Licensing _AT_ CodeGNU _DOT_ com> 0.3.0 - Unregistering repositories will now only do so on an exact match of the storage name.
--- a/doc/TODO Mon Mar 09 20:42:19 2015 +0100 +++ b/doc/TODO Mon Mar 09 21:24:51 2015 +0100 @@ -35,5 +35,4 @@ * Create a separate manager for the HgWeb configuration file. - Manager should provide a REST API for repository management. - Manager should have an API key per user permission values. -- Change functions to use exceptions for error handling, except for the command-line module. - Prevent a push that would create multiple heads
--- a/setup.cfg Mon Mar 09 20:42:19 2015 +0100 +++ b/setup.cfg Mon Mar 09 21:24:51 2015 +0100 @@ -33,7 +33,6 @@ group = Development requires = python mercurial - filelock doc_files = README LICENSE doc/ChangeLog
--- a/src/__init__.py Mon Mar 09 20:42:19 2015 +0100 +++ b/src/__init__.py Mon Mar 09 21:24:51 2015 +0100 @@ -18,16 +18,3 @@ *** You should have received a copy of the GNU Affero General Public License*** *** along with this program. If not, see <http://www.gnu.org/licenses/>. *** *****************************************************************************''' - - -# Files to include when creating the package. -__all__ = \ - [ - '_app_info.py', - 'config.py', - 'enum.py', - 'hwm.py', - 'ignorepo.py', - 'manager.py', - 'manrepo.py' - ]
--- a/src/_app_info.py Mon Mar 09 20:42:19 2015 +0100 +++ b/src/_app_info.py Mon Mar 09 21:24:51 2015 +0100 @@ -27,10 +27,10 @@ LONG_NAME = 'HgWeb Manager' # The current version of the application -VERSION = '0.3.0' +VERSION = '0.4.0' # The current copyright years. -YEARS = '2014' +YEARS = '2014 - 2015' # A short description of the application DESCRIPTION = 'Manages HgWeb Repositories'
--- a/src/config.py Mon Mar 09 20:42:19 2015 +0100 +++ b/src/config.py Mon Mar 09 21:24:51 2015 +0100 @@ -58,6 +58,9 @@ """ Loads the settings values stored in the configuration file. @param[in] path The path and file name of the configuration file. + + @throws ConfigParser.Error When the configuration file cannot be + found. """ from ConfigParser import SafeConfigParser, Error
--- a/src/hwm.py Mon Mar 09 20:42:19 2015 +0100 +++ b/src/hwm.py Mon Mar 09 21:24:51 2015 +0100 @@ -21,24 +21,11 @@ *****************************************************************************''' -""" -IMPORTS -""" -import config -import _app_info - +def is_hgWeb_user(expectedHgUser): + """ Determines if the current running user is the expected HgWeb user. -try: - settings = config.Manager() -except Exception as e: - import sys - - print >>sys.stderr, 'Settings Error:', e - exit(1) - - -def is_hgWeb_user(): - """ Determines if the current running user is the expected HgWeb user. + @param[in] str expectedHgUser The expected user-name for the Mercurial + web-manager. @return Gives true when the user is the expected HgWeb user, false else-wise. @@ -46,7 +33,7 @@ import getpass # FIXME: This isn't a secure check as it is based upon the environmental variable. - if settings.HgUser == getpass.getuser(): + if expectedHgUser == getpass.getuser(): return True else: return False @@ -86,65 +73,66 @@ return managed - -def main(args): - import argparse - import manager - import sys +if __name__ == '__main__': + import _app_info - if not is_hgWeb_user(): - print >>sys.stderr, 'Must execute as the Mercurial Web manager.' - exit(1) - - parser = argparse.ArgumentParser(description=_app_info.DESCRIPTION) - parser.add_argument('-l', '--list', action='store_true', help='Outputs a list of all the managed repositories.') + try: + # Display license + print "%s %s Copyright (C) %s CodeGNU Solutions" % \ + (_app_info.LONG_NAME, _app_info.VERSION, _app_info.YEARS) + print "%s comes with ABSOLUTELY NO WARRANTY;" % (_app_info.SHORT_NAME) + print 'This is free software, and you are welcome to redistribute it' + print 'under certain conditions; see the LICENSE file for details,' + print 'or the Free Software Foundation\'s AGPL.\n' - manGrp = parser.add_argument_group('Manage', 'Options that operate upon managed repositories.') - manGrp.add_argument('storage', action='store', nargs='?', help='The storage-name of the repository to perform an action upon.') - manGrp.add_argument('-a', '--action', action='store', nargs=1, type=str, choices=['c', 'create', 'm', 'modify', 'd', 'delete', 'r', 'register'], help='Performs an action upon a managed repository.') - manGrp.add_argument('-i', '--ignore', action='store', nargs='+', default=None, help='The groups to place in an ignore file.') - manGrp.add_argument('-s', '--skip-common', action='store_true', help='The common ignore group is not added by default.') - manGrp.add_argument('-n', '--name', action='store', nargs='?', default=None, help='The display name of the repository.') - manGrp.add_argument('-d', '--description', action='store', nargs='?', default=None, help='The description of the repository.') - manGrp.add_argument('-c', '--contact', action='store', nargs='?', default=None, help='The contact point of a repository.') - manGrp.add_argument('-r', '--rename', action='store', nargs='?', default=None, help='The new storage-name for a repository.') + from manager import settings + import argparse + import manager + import sys - # Examine the supplied arguments and perform requested actions. - args = parser.parse_args() - repo = __extract_values(args) + if not is_hgWeb_user(settings.HgUser): + print >>sys.stderr, 'Must execute as the Mercurial Web manager.' + exit(1) + + parser = argparse.ArgumentParser(description=_app_info.DESCRIPTION) + parser.add_argument('-l', '--list', action='store_true', help='Outputs a list of all the managed repositories.') - if args.list: - if not manager.list(): - exit(2) - elif args.action is None: - print >>sys.stderr, "No actionable arguments supplied." - exit(1) - elif ('create' == args.action[0]) or ('c' == args.action[0]): - if not manager.create(repo, args.ignore, args.skip_common): - exit(2) - elif ('modify' == args.action[0]) or ('m' == args.action[0]): - if not manager.modify(repo, args.storage, args.ignore, args.skip_common): - exit(2) - elif ('delete' == args.action[0]) or ('d' == args.action[0]): - if not manager.delete(repo): - exit(2) - elif ('register' == args.action[0]) or ('r' == args.action[0]): - if not manager.register(repo): - exit(2) - else: - print >>sys.stderr, "Failure determine requested action '%s'." % args.action[0] + manGrp = parser.add_argument_group('Manage', 'Options that operate upon managed repositories.') + manGrp.add_argument('storage', action='store', nargs='?', help='The storage-name of the repository to perform an action upon.') + manGrp.add_argument('-a', '--action', action='store', nargs=1, type=str, choices=['c', 'create', 'm', 'modify', 'd', 'delete', 'r', 'register'], help='Performs an action upon a managed repository.') + manGrp.add_argument('-i', '--ignore', action='store', nargs='+', default=None, help='The groups to place in an ignore file.') + manGrp.add_argument('-s', '--skip-common', action='store_true', help='The common ignore group is not added by default.') + manGrp.add_argument('-n', '--name', action='store', nargs='?', default=None, help='The display name of the repository.') + manGrp.add_argument('-d', '--description', action='store', nargs='?', default=None, help='The description of the repository.') + manGrp.add_argument('-c', '--contact', action='store', nargs='?', default=None, help='The contact point of a repository.') + manGrp.add_argument('-r', '--rename', action='store', nargs='?', default=None, help='The new storage-name for a repository.') + + # Examine the supplied arguments and perform requested actions. + args = parser.parse_args() + repo = __extract_values(args) + + if args.list: + manager.list() + elif args.action is None: + print >>sys.stderr, "No actionable arguments supplied." + exit(1) + elif ('create' == args.action[0]) or ('c' == args.action[0]): + manager.create(repo, args.ignore, args.skip_common) + elif ('modify' == args.action[0]) or ('m' == args.action[0]): + manager.modify(repo, args.storage, args.ignore, args.skip_common) + elif ('delete' == args.action[0]) or ('d' == args.action[0]): + manager.delete(repo) + elif ('register' == args.action[0]) or ('r' == args.action[0]): + manager.register(repo) + else: + print >>sys.stderr, "Failure determine requested action '%s'." % args.action[0] + exit(1) + exit(0) + except (ValueError, RuntimeError) as error: + print >>sys.stderr, "Issue Discovered: %s" % error exit(2) - exit(0) - -if __name__ == '__main__': - import sys + except Exception as error: + import traceback - # Display license - print "%s %s Copyright (C) %s CodeGNU Solutions" % \ - (_app_info.LONG_NAME, _app_info.VERSION, _app_info.YEARS) - print "%s comes with ABSOLUTELY NO WARRANTY;" % (_app_info.SHORT_NAME) - print 'This is free software, and you are welcome to redistribute it' - print 'under certain conditions; see the LICENSE file for details,' - print 'or the Free Software Foundation\'s AGPL.\n' - - main(sys.argv) + print >>sys.stderr, "Unexpected error: %s\n" % error, '-' * 60, '\n', traceback.format_exc(), '-' * 60 + exit(3)
--- a/src/ignorepo.py Mon Mar 09 20:42:19 2015 +0100 +++ b/src/ignorepo.py Mon Mar 09 21:24:51 2015 +0100 @@ -19,10 +19,6 @@ *** along with this program. If not, see <http://www.gnu.org/licenses/>. *** *****************************************************************************''' - -""" -IMPORTS -""" import enum @@ -83,6 +79,8 @@ @param[in] desc The description of the ignore entry. @param[in] fil The type of ignore filtering. @param[in] val The ignore expression. + + @throws ValueError When the supplied ignore filter is invalid. """ self.__description = desc.strip() if fil == 'glob': @@ -90,7 +88,7 @@ elif fil == 'regex': self.__filter = FilterType.RegularExpression else: - raise Exception("Invalid filter type '%s' supplied" % fil) + raise ValueError("Invalid filter type '%s' supplied" % fil) self.__value = val.strip() @@ -104,21 +102,25 @@ @param[in] ignoreFile The directory path and file name to an XML file that contains the ignore patterns to extract. - @throw Exception When the name of a group is reused + @throw ValueError When the name of a group is reused in the XML file. - @throw xml.parsers.expat.ExpatError When the XML file cannot be parsed. + @throw When the XML file cannot be parsed. @return The extracted grouped ignore patterns. """ from xml.dom import minidom + from xml.parsers.expat import ExpatError ignores = {} - xmlIgnore = minidom.parse(ignoreFile) + try: + xmlIgnore = minidom.parse(ignoreFile) + except ExpatError as error: + raise RuntimeError(error) for group in xmlIgnore.getElementsByTagName('ignores')[0].getElementsByTagName('group'): groupName = group.getAttribute('name') if groupName in ignores: - raise Exception("The group '%s' was already defined." % groupName) + raise ValueError("The group '%s' was already defined." % groupName) else: ignores[groupName] = [] for pattern in group.getElementsByTagName('pattern'):
--- a/src/manager.py Mon Mar 09 20:42:19 2015 +0100 +++ b/src/manager.py Mon Mar 09 21:24:51 2015 +0100 @@ -19,11 +19,11 @@ *** along with this program. If not, see <http://www.gnu.org/licenses/>. *** *****************************************************************************''' +from manrepolock import RepositoryManagerLock +import config -""" -IMPORTS -""" -from hwm import settings + +settings = config.Manager() def create(repository, ignoreGroups, skipCommon): @@ -40,49 +40,36 @@ that is the newly generated repository. The new repository is registered in HgWeb to display and is secured to prevent accidental modification. - @return When the creation is successful gives true, else-wise false. + @throws ValueError When the repository storage or display name exists. + @throws RuntimeError When the new repository creation encounters an + error. """ - import sys import os import shutil import stat with RepositoryManagerLock(): - print "Adding repository: %s" % repository.StorageName + print "\tAdding repository: %s" % repository.StorageName if __doesRepositoryStorageNameExist(repository): - print >>sys.stderr, "The repository storage name '%s' already exists." % repository.StorageName - return False + raise ValueError("The repository storage name '%s' already exists." % repository.StorageName) if __doesRepositoryDisplayNameExist(repository): - print >>sys.stderr, "The display name '%s' already exists." % repository.DisplayName - return False - - # Create the requested repository. - if __addRepository(repository): - print "Initialised repository directory." - if not __create_ignores(repository, ignoreGroups, skipCommon): - __removeRepository(repository) - return False - else: - print >>sys.stderr, "Failed to create repository directory." - return False + raise ValueError("The display name '%s' already exists." % repository.DisplayName) - # Fill out the details of about the repository. - if __writeRepositoryDetails(repository): - print "Successfully wrote display details." - else: - print >>sys.stderr, "Failed to write display details." - os.chmod(settings.RepositoryPath + os.sep + repository.StorageName + os.sep + '.hg' + os.sep + "hgrc", stat.S_IWRITE) + try: + __addRepository(repository) + __create_ignores(repository, ignoreGroups, skipCommon) + except RuntimeError: + __removeRepository(repository) + pass + + try: + __writeRepositoryDetails(repository) + __registerRepository(repository) + except RuntimeError: + os.chmod(os.path.join(settings.RepositoryPath, repository.StorageName, '.hg', "hgrc"), stat.S_IWRITE) shutil.rmtree(settings.RepositoryPath + os.sep + repository.StorageName) - return False - - if __registerRepository(repository): - print 'Repository added.' - return True - else: - os.chmod(settings.RepositoryPath + os.sep + repository.StorageName + os.sep + '.hg' + os.sep + "hgrc", stat.S_IWRITE) - shutil.rmtree(settings.RepositoryPath + os.sep + repository.StorageName) - return False + pass def register(repository): @@ -95,21 +82,17 @@ @post The new repository is registered in HgWeb to display and is secured to prevent accidental modification. - @return When the registration is successful gives true, else-wise false. + @throws ValueError When the repository storage or display name does not + exist. + @throws RuntimeError When the new repository registration encounters an + error. """ with RepositoryManagerLock(): - print "Registering the repository: %s" % repository.StorageName - if not __doesRepositoryStorageNameExist(repository): - print >>sys.stderr, "The repository '%s' does not exists." % repository.StorageName - return False - - # Add the new repository to the web registry. - if __registerRepository(repository): - print "Successfully registered repository." - return True + print "\tRegistering the repository: %s" % repository.StorageName + if __doesRepositoryStorageNameExist(repository): + __registerRepository(repository) else: - print >>sys.stderr, "Failed to register repository." - return False + raise ValueError("The repository '%s' does not exists." % repository.StorageName) def modify(newRepository, currentStorageName, ignoreGroups, skipCommon): @@ -128,68 +111,46 @@ @post The repository is changed with the requested modifications. - @return When the modification is successful gives true, else-wise false. + @throws ValueError When the repository storage name does not exist. + @throws RuntimeError When the new repository modification encounters an + error. """ - import sys from manrepo import Repository with RepositoryManagerLock(): - print "Modifying repository: %s" % currentStorageName + print "\tModifying repository: %s" % currentStorageName oldRepository = Repository(currentStorageName) if not __doesRepositoryStorageNameExist(oldRepository): - print >>sys.stderr, "The repository '%s' was not found." % oldRepository.StorageName - return False + raise ValueError("The repository '%s' was not found." % oldRepository.StorageName) - # Remove the current repository from the web registry. - if not __unregisterRepository(oldRepository): - print >>sys.stderr, "Failed to unregister repository." - return False + __unregisterRepository(oldRepository) if __doesRepositoryDisplayNameExist(newRepository): - print >>sys.stderr, "The display name '%s' already exists." % newRepository.DisplayName - if not __registerRepository(oldRepository): - print >>sys.stderr, "Failed to register repository." - return False + __registerRepository(oldRepository) + raise ValueError("The display name '%s' already exists." % newRepository.DisplayName) # Update renamed repositories if not oldRepository == newRepository: - if __renameStorage(oldRepository, newRepository.StorageName): - print "Renamed repository %s to %s." % (currentStorageName, newRepository.StorageName) - else: - print >>sys.stderr, "Failed to rename repository." - if __registerRepository(oldRepository): - print "Successfully rolled backed action." - else: - print >>sys.stderr, "Failed to roll back action." - return False + try: + __renameStorage(oldRepository, newRepository.StorageName) + except RuntimeError: + __registerRepository(oldRepository) + pass # Fill out the details of about the new repository. - if __writeRepositoryDetails(newRepository): - print "Successfully wrote new details." - else: - print >>sys.stderr, "Failed to write new details." - if __registerRepository(oldRepository): - print "Successfully rolled backed action." - else: - print >>sys.stderr, "Failed to roll back action." - return False - - # Update ignore file if requested. - if ignoreGroups is not None: - if __create_ignores(newRepository, ignoreGroups, skipCommon): - print "Successfully modified ignore file." - else: - print >>sys.stderr, "Failed to modify ignore file." - return False + try: + __writeRepositoryDetails(newRepository) + # Update ignore file if requested. + if ignoreGroups is not None: + __create_ignores(newRepository, ignoreGroups, skipCommon) + except RuntimeError: + __registerRepository(oldRepository) + print "Successfully rolled backed action(s)." + pass # Add the new repository to the web registry. - if __registerRepository(newRepository): - print 'Repository modified.' - return True - else: - print >>sys.stderr, "Failed to register repository." - return False + __registerRepository(newRepository) def delete(repository): @@ -203,48 +164,28 @@ path that is the supplied repository. The repository is no longer registered in HgWeb. - @return When the deletion is successful gives true, else-wise false. + @throws ValueError When the repository storage name does not exist. """ with RepositoryManagerLock(): if not __doesRepositoryStorageNameExist(repository): - print >>sys.stderr, "The repository %s was not found." % repository.StorageName - return False + raise ValueError("The repository %s was not found." % repository.StorageName) - print "Removing repository: %s" % repository.StorageName - if __unregisterRepository(repository): - print "Successfully unregistered repository." - else: - print >>sys.stderr, "Failed to unregister repository." - - if __removeRepository(repository): - print "Repository removed." - return True - else: - print >>sys.stderr, "Failed to delete the repository." - return False - print >>sys.stderr, "The HgWeb configuration file does not have a paths section!" + print "\tRemoving repository: %s" % repository.StorageName + __unregisterRepository(repository) + __removeRepository(repository) def list(): - """ Outputs a listing of all the known managed repositories - - @return When the output generation is successful gives true, else-wise false. - """ + """ Outputs a listing of all the known managed repositories """ import manrepo print "Managed Repositories:" - try: - repoCol = manrepo.ManagedCollection() - for repo in repoCol.Repositories: - print "\t%s (%s)" % (repo.StorageName, repo.DisplayName) - return True - except Exception as e: - import sys + repoCol = manrepo.ManagedCollection() + for repo in repoCol.Repositories: + print "\t%s (%s)" % (repo.StorageName, repo.DisplayName) - print >>sys.stderr, "Listing error({0}): {1}".format(e.errno, e.strerror) - return False -# Functions for fulfilling repository management. +# Functions For Fulfilling Repository Management ### def __create_ignores(repo, ignoreGroups, skipCommon): @@ -262,25 +203,15 @@ @post The managed repository now has an ignore file committed by the HgWeb manager. - @return When the creation is successful gives true, else-wise false. + @throws RuntimeError When the list of ignores does not have a group. """ import os import ignorepo - import sys - import xml # Extract Ignore Collection if ignoreGroups is None: ignoreGroups = [] - ignores = {} - try: - ignores = ignorepo.extract('ignores.xml') - except IOError as e: - print >>sys.stderr, "Failed to locate ignores, error({0}): {1}".format(e.errno, e.strerror) - return False - except xml.parsers.expat.ExpatError as e: - print >>sys.stderr, "Failed to extract ignores, error: %s" % e.message - return False + ignores = ignorepo.extract('ignores.xml') if skipCommon: del ignores['Common'] @@ -290,8 +221,7 @@ if '+' not in ignoreGroups: for ignoreGroup in ignoreGroups: if not ignoreGroup.lower() in [ignore.lower() for ignore in ignores]: - print >>sys.stderr, "The list of ignores does not have the '%s' group." % ignoreGroup - return False + raise RuntimeError("The list of ignores does not have the '%s' group." % ignoreGroup) ignoreFile = settings.RepositoryPath + os.sep + repo.StorageName + os.sep + '.hgignore' wasCreated = not os.path.isfile(ignoreFile) @@ -320,18 +250,11 @@ hgIgnore.write('\n\n') if wasCreated: - if __addFile(repo, '.hgignore'): - print "Successfully added generated ignores." - else: - print >>sys.stderr, "Failed to add generated ignores." - return False + print "Adding generated ignores ..." + __addFile(repo, '.hgignore') - if __commitChanges(repo, 'Created generated ignores filter.'): - print "Successfully committed generated ignores." - else: - print >>sys.stderr, "Failed to commit generated ignores." - return False - return True + print "Committing generated ignores ..." + __commitChanges(repo, 'Created generated ignores filter.') def __commitChanges(repo, message): @@ -345,19 +268,16 @@ @post Upon any error the standard error buffer contains an error message. - @return When the process is successful gives true, else-wise false. + @throws RuntimeError When the added files fail to commit. """ import subprocess - import sys import os - try: - statusCode = subprocess.call([settings.HgCommand, '--repository', settings.RepositoryPath + os.sep + repo.StorageName, 'commit', '--message', message, '--user', settings.HgUser + ' - HgWeb Manager']) - except OSError as e: - print >>sys.stderr, "Commit Error({0}): {1}".format(e.errno, e.strerror) - return False + statusCode = subprocess.call([settings.HgCommand, '--repository', settings.RepositoryPath + os.sep + repo.StorageName, 'commit', '--message', message, '--user', settings.HgUser + ' - HgWeb Manager']) + # Zero being a successful commit and one being nothing changed. - return ((0 == statusCode) or (1 == statusCode)) + if not ((0 == statusCode) or (1 == statusCode)): + raise RuntimeError("Failed to commit files. Status code: " + statusCode) def __addFile(repo, fileName): @@ -371,18 +291,16 @@ @post Upon any error the standard error buffer contains an error message. - @return When the process is successful gives true, else-wise false. + @throws RuntimeError When the the news files fail to be added. """ import subprocess - import sys import os - try: - statusCode = subprocess.call([settings.HgCommand, '--repository', settings.RepositoryPath + os.sep + repo.StorageName, 'add', settings.RepositoryPath + os.sep + repo.StorageName + os.sep + fileName]) - except OSError as e: - print >>sys.stderr, "Add Error({0}): {1}".format(e.errno, e.strerror) - return False - return (0 == statusCode) + statusCode = subprocess.call([settings.HgCommand, '--repository', settings.RepositoryPath + os.sep + repo.StorageName, 'add', settings.RepositoryPath + os.sep + repo.StorageName + os.sep + fileName]) + + # Zero being a successful commit and one being nothing to add. + if not ((0 == statusCode) or (1 == statusCode)): + raise RuntimeError("Failed to commit files. Status code: " + statusCode) def __renameStorage(oldRepo, newName): @@ -392,25 +310,16 @@ expected to be changed. @param[in] newName The new storage name for the repository. - @return Gives true when the storage name is successfully renamed, - else-wise gives false. + @throws ValueError When the supplied new storage name already exists. """ import os - import sys import shutil - try: - if oldRepo.StorageName == newName: - return True - elif os.path.isdir(settings.RepositoryPath + os.sep + newName): - print >>sys.stderr, "Cannot rename the existing repository '%s' because the name '%s' already exists." % (oldRepo.StorageName, newName) - return False - else: - shutil.move(settings.RepositoryPath + os.sep + oldRepo.StorageName, settings.RepositoryPath + os.sep + newName) - except OSError as e: - print >>sys.stderr, "Rename error({0}): {1}".format(e.errno, e.strerror) - return False - return True + if not oldRepo.StorageName == newName: + print "Renaming repository %s to %s ..." % (oldRepo.StorageName, newName) + if os.path.isdir(settings.RepositoryPath + os.sep + newName): + raise ValueError("Cannot rename the existing repository '%s' because the name '%s' already exists." % (oldRepo.StorageName, newName)) + shutil.move(settings.RepositoryPath + os.sep + oldRepo.StorageName, settings.RepositoryPath + os.sep + newName) def __doesRepositoryStorageNameExist(repo): @@ -458,16 +367,16 @@ and is returned to a read-only state. Upon any error the standard error buffer contains an error message. - @return When the process is successful gives true, else-wise false. + @throws RuntimeError When the web path is missing the configuration file. + @throws RuntimeError When the configuration file does not contain a path + section. """ from ConfigParser import SafeConfigParser import os - import sys parser = SafeConfigParser() if not parser.read(settings.HgWebPath + os.sep + 'hgweb.config'): - print >>sys.stderr, "Failed to locate HgWeb configuration file." - return False + raise RuntimeError("Failed to locate HgWeb configuration file.") if parser.has_section('paths'): if parser.has_option('paths', repo.StorageName): @@ -475,13 +384,10 @@ parser.remove_option('paths', repo.StorageName) with open(settings.HgWebPath + os.sep + 'hgweb.config', 'wb') as configfile: parser.write(configfile) - return True else: print "The repository already is unregistered." - return True else: - print >>sys.stderr, "The HgWeb configuration file does not have a paths section!" - return False + raise RuntimeError("The HgWeb configuration file does not have a paths section! Failed to unregister repository.") def __removeRepository(repo): @@ -492,24 +398,19 @@ @param[in] repo The targeted repository for which to delete. @post Upon any error the standard error buffer contains an error message. - - @return When the process is successful gives true, else-wise false. """ import shutil import os import stat + print "Removing repository directories ..." try: os.chmod(settings.RepositoryPath + os.sep + repo.StorageName + os.sep + '.hg' + os.sep + "hgrc", stat.S_IWRITE) except OSError as e: - # If it's not the "No such file or directory", notify the user. + # If it's anything but the "No such file or directory", notify the user. if not 2 == e.errno: - import sys - - print >>sys.stderr, "Repository removal error({0}): {1}".format(e.errno, e.strerror) - return False + pass shutil.rmtree(settings.RepositoryPath + os.sep + repo.StorageName) - return True def __addRepository(repo): @@ -521,22 +422,18 @@ @post Upon any error the standard error buffer contains an error message. - @return When the process is successful gives true, else-wise false. + @throws ValueError When the storage name is missing. + @throws RuntimeError When the initialisation call fails. """ import subprocess - import sys import os + print "Initialising repository directory ..." if repo.StorageName is None: - print >>sys.stderr, 'Cannot create a managed repository without a storage name.' - return False - - try: - statusCode = subprocess.call([settings.HgCommand, 'init', settings.RepositoryPath + os.sep + repo.StorageName]) - except OSError as e: - print >>sys.stderr, "Initialisation error({0}): {1}".format(e.errno, e.strerror) - return False - return (0 == statusCode) + raise ValueError('Cannot create a managed repository without a storage name.') + statusCode = subprocess.call([settings.HgCommand, 'init', settings.RepositoryPath + os.sep + repo.StorageName]) + if not (0 == statusCode): + raise RuntimeError("Failed to initialise repository. Status code: " + statusCode) def __writeRepositoryDetails(repo): @@ -548,8 +445,6 @@ @post The HgWeb configuration file contains the new repository details and is returned to a read-only state. - - @return When the process is successful gives true, else-wise false. """ import os import stat @@ -557,49 +452,35 @@ print "Saving display details ..." config = settings.RepositoryPath + os.sep + repo.StorageName + os.sep + '.hg' + os.sep + 'hgrc' + if os.path.exists(config): hgrcMode = os.stat(config)[stat.ST_MODE] os.chmod(config, hgrcMode | stat.S_IWRITE) else: hgrcMode = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH - - try: - repo.save() - except IOError as e: - print >>sys.stderr, "Failed to save details, error({0}): {1}".format(e.errno, e.strerror) - return False + repo.save() # Ensure the repository details isn't accidentally modified. os.chmod(config, hgrcMode & (~stat.S_IWUSR & ~stat.S_IWGRP & ~stat.S_IWOTH)) # Ensure the repository isn't accidentally deleted, moved, or blocked in Windows. if "win32" == sys.platform: - if __guardWindowsRepository(repo): - print 'Secured repository store in Windows.' - else: - print >>sys.stderr, 'Failed to secure repository store in Windows.' - return False - return True + __guardWindowsRepository(repo) + print 'Secured repository store in Windows.' def __guardWindowsRepository(repo): """ Guards the supplied repository for a repository on a Windows system. @param[in] repo The targeted repository for which to secure. - - @return When the process is successful gives true, else-wise false. """ import subprocess import os - try: - print 'Securing for Windows ...' - status = subprocess.call(["attrib.exe", '+H', '+I', '+S', settings.RepositoryPath + os.sep + repo.StorageName + os.sep + '.hg']) - except OSError as e: - import sys + print 'Securing for Windows ...' + status = subprocess.call(["attrib.exe", '+H', '+I', '+S', settings.RepositoryPath + os.sep + repo.StorageName + os.sep + '.hg']) - print >>sys.stderr, "Windows Guard error({0}): {1}".format(e.errno, e.strerror) - return False - return (0 == status) + if not (0 == status): + raise RuntimeError("Failed to secure repository store for windows. Status code: " + status) def __registerRepository(repo): @@ -611,25 +492,22 @@ is returned to a read-only state. Upon any error the standard error buffer contains an error message. - @return When the process is successful gives true, else-wise false. + @throws RuntimeError When the HgWeb configuration file cannot be found. + @throws ValueError When the supplied repository is already registered. """ from ConfigParser import SafeConfigParser import os - import sys parser = SafeConfigParser() if not parser.read(settings.HgWebPath + os.sep + 'hgweb.config'): - print >>sys.stderr, "Failed to locate HgWeb configuration file." - return False + raise RuntimeError("Failed to locate HgWeb configuration file.") if parser.has_section('paths'): if parser.has_option('paths', repo.StorageName): - print >>sys.stderr, "The repository already is registered." - return False + raise ValueError("The repository already is registered.") else: parser.add_section('paths') - print >>sys.stderr, "Registering repository ..." + print "Registering repository ..." parser.set('paths', repo.StorageName, settings.RepositoryPath + os.sep + repo.StorageName) with open(settings.HgWebPath + os.sep + 'hgweb.config', 'wb') as configfile: parser.write(configfile) - return True
--- a/src/manrepo.py Mon Mar 09 20:42:19 2015 +0100 +++ b/src/manrepo.py Mon Mar 09 21:24:51 2015 +0100 @@ -19,11 +19,7 @@ *** along with this program. If not, see <http://www.gnu.org/licenses/>. *** *****************************************************************************''' - -""" -IMPORTS -""" -from hwm import settings +from manager import settings class Repository(object): @@ -157,14 +153,14 @@ def __init__(self): """ Initialises the container with all the managed repositories. - @throws Exception When the HgWeb configuration cannot be read. + @throws RuntimeError When the HgWeb configuration cannot be read. """ from ConfigParser import SafeConfigParser import os parser = SafeConfigParser() if not parser.read(settings.HgWebPath + os.sep + 'hgweb.config'): - raise Exception("Failed to read HgWeb configuration.") + raise RuntimeError("Failed to read HgWeb configuration.") for name, path in parser.items('paths'): self.__repositories.append(Repository(name))