From 5156d1d7635806a237bdf9857702068290fc6340 Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Tue, 20 Apr 2010 23:02:42 -0400 Subject: First pass at the basic client --- .hgignore | 2 + obalie/__init__.py | 0 obalie/client.py | 92 +++++++++++++++++++++++++++++++++++++++++++++ obalie/commands/__init__.py | 0 obalie/exceptions.py | 27 +++++++++++++ obalie/log.py | 44 ++++++++++++++++++++++ obalie/utils.py | 32 ++++++++++++++++ 7 files changed, 197 insertions(+) create mode 100644 .hgignore create mode 100644 obalie/__init__.py create mode 100644 obalie/client.py create mode 100644 obalie/commands/__init__.py create mode 100644 obalie/exceptions.py create mode 100644 obalie/log.py create mode 100644 obalie/utils.py diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..a26d142 --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +syntax:glob +*.pyc diff --git a/obalie/__init__.py b/obalie/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/obalie/client.py b/obalie/client.py new file mode 100644 index 0000000..d943bb8 --- /dev/null +++ b/obalie/client.py @@ -0,0 +1,92 @@ +# vim: set filencoding=utf8 +""" +Subversion Client + +@author: Mike Crute (mcrute@ag.com) +@organization: SoftGroup Interactive, Inc. +@date: April 20, 2010 +""" + +from urlparse import urlparse +from obalie.commands import load_commands +from obalie.exceptions import UnsupportedCommand +from obalie.log import inject_logger +from obalie.utils import run_command + + +class Client(object): + + @inject_logger + def __init__(self, repo_url, trust_server=True, config_dir=None, + svn_command='svn', logger=None, **config): + self.logger = logger + self.command = svn_command + self.config_dir = config_dir + self.trust_server = trust_server + self.additional_config = config + self.verbosity = None + self.commands = {} + + self._unpack_url(repo_url) + + def __getattr__(self, name): + """ + Proxy commands through to a command object. + """ + if not self.commands: + self.commands = load_commands() + + try: + cmd = self.commands[name](self) + return cmd + except KeyError: + raise UnsupportedCommand(name) + + def _unpack_url(self, url): + """ + Parses a repository URL and loads its parts into the instance. + """ + parsed = urlparse(url) + + self.repo_url = "{0}://{1}".format(parsed.scheme, parsed.hostname) + if parsed.port: + self.repo_url += ":{0}".format(parsed.port) + self.repo_url += parsed.path + + self.logger.debug('Repository URL: %r', self.repo_url) + + self.username = parsed.username + self.password = parsed.password + + def _get_svn_args(self): + """ + Gets a string of global options for the subversion command. + """ + args = [] + if self.username: + args.append('--username={0}'.format(self.username)) + + if self.password: + args.append('--password={0}'.format(self.password)) + args.append('--no-auth-cache') + + if self.config_dir: + args.append('--config-dir={0}'.format(self.config_dir)) + + if self.trust_server: + args.append('--trust-server-cert') + args.append('--non-interactive') + + for key, value in self.additional_config.items(): + option = '{0}={1}'.format(key, value) + args.append('--config-option={0}'.format(option)) + + return ' '.join(args) + + def run_raw_command(self, subcommand, *args): + command = [self.command, self._get_svn_args(), subcommand] + command = ' '.join(command + list(args)) + + self.logger.debug("Command: %r", command) + + return run_command(command) diff --git a/obalie/commands/__init__.py b/obalie/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/obalie/exceptions.py b/obalie/exceptions.py new file mode 100644 index 0000000..c7cbb4d --- /dev/null +++ b/obalie/exceptions.py @@ -0,0 +1,27 @@ +# vim: set filencoding=utf8 +""" +Subversion Client Exceptions + +@author: Mike Crute (mcrute@ag.com) +@organization: SoftGroup Interactive, Inc. +@date: April 20, 2010 +""" + + +class SubversionError(Exception): + pass + + +class ExecutableError(SubversionError): + + def __init__(self, return_code, command): + self.return_code = return_code + self.command = command + + def __str__(self): + return "Command {0!r} returned with non-zero status: {1}".format( + self.command, self.return_code) + + +class UnsupportedCommand(SubversionError): + pass diff --git a/obalie/log.py b/obalie/log.py new file mode 100644 index 0000000..bf65996 --- /dev/null +++ b/obalie/log.py @@ -0,0 +1,44 @@ +# vim: set filencoding=utf8 +""" +Logging Utlities for obalie + +@author: Mike Crute (mcrute@ag.com) +@organization: SoftGroup Interactive, Inc. +@date: April 20, 2010 +""" + + +import sys +import logging +from logging import Formatter, StreamHandler +from functools import wraps + + +# Setup root logger +root_logger = logging.getLogger('obalie') + +# If the root logger is not already configured then we +# should probably configure it with some sane defaults. +if root_logger.level == logging.NOTSET: +# XXX: RESET THIS! +# root_logger.setLevel(logging.WARN) + root_logger.setLevel(logging.DEBUG) + + handler = StreamHandler(sys.stdout) + formatter = Formatter('%(asctime)s %(levelname)s %(name)s %(message)s') + + handler.setFormatter(formatter) + root_logger.addHandler(handler) + + +def inject_logger(cls): + """ + Injects a logger into the kwargs for a class. + """ + @wraps(cls) + def decorator(*args, **kwargs): + inst = args[0] # self + kwargs['logger'] = logging.getLogger('.'.join([inst.__module__, inst.__class__.__name__])) + cls(*args, **kwargs) + + return decorator diff --git a/obalie/utils.py b/obalie/utils.py new file mode 100644 index 0000000..dcf476a --- /dev/null +++ b/obalie/utils.py @@ -0,0 +1,32 @@ +# vim: set filencoding=utf8 +""" +Miscellaneous Utility Commands + +@author: Mike Crute (mcrute@ag.com) +@organization: SoftGroup Interactive, Inc. +@date: April 20, 2010 +""" + + +import shlex +from subprocess import Popen, PIPE +from obalie.exceptions import ExecutableError + + +def run_command(command, raw_output=False): + """ + Run a command string returning the output. Passing raw_output + will cause the function to return the raw stdout; otherwise + lines will be split. + """ + cmd = shlex.split(command.encode('ascii')) + proc = Popen(cmd, close_fds=False, stdout=PIPE, stderr=PIPE) + stdout, stderr = proc.communicate() + + if proc.returncode != 0: + raise ExecutableError(proc.returncode, command) + + if raw_output: + return stdout + else: + return stdout.splitlines(True) -- cgit v1.2.3