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))