ucltip-0.7.1/0000775000175000017500000000000011674112161013577 5ustar hychenhychen00000000000000ucltip-0.7.1/AUTHORS0000664000175000017500000000026411674103616014656 0ustar hychenhychen00000000000000UCLTIP was modified from lib/git/cmd.py file in GitPython-0.1.6 by Hsin-Yi Chen GitPython was originally written by Michael Trier ucltip-0.7.1/ChangeLog.txt0000664000175000017500000000266311674112145016200 0ustar hychenhychen000000000000002011-12-20 Hsin-Yi Chen - 0.7.1 * Fix: * Missing VERSION.txt, MANIFEST.in 2011-12-20 Hsin-Yi Chen - 0.7 * New features: * global_config to change variables - via_shell: set all command executed in os.system * support to specifcy current directory when running a command * regcmds can recongize the commands shold be use CmdDispatcher class (very limitated) * New Test Case * GlobalConfigTestCase * Enhancement: * CommandNotFound to knows which command be used * Fix: * HelperTestCase don't reset __builtin__ to default * setup.py - does not include non-ucltip python file in tar bar 2011-10-03 Hsin-Yi Chen - 0.6 * New features: * avaliable for setting global configure * pipeline * New Class: Pipe * New Function:global_config * Enhance class customiz way * Rename Paramater: interact prammater -> as_process in ExecutableCmd.execute * Update README.txt 2011-06-28 Hsin-Yi Chen - 0.5 * New features: * create a combined command string for testing * setting default command options * New Class: CmdConfiguration, SubCmd * Rename Function: reg_singlecmds -> regcmds * Rename Class: SingleCmd -> Cmd * Change copyright year 2010 to 2011 * New Test: ExecutedCmd, UtilsTestCase, SubCmdTestCase, CmdDispatcherTestCase, CustomClassTestCase, HelperTestCase * Upadte Readme.txt * Update comments ucltip-0.7.1/ucltip/0000775000175000017500000000000011674112161015077 5ustar hychenhychen00000000000000ucltip-0.7.1/ucltip/__init__.py0000664000175000017500000004077611674111601017224 0ustar hychenhychen00000000000000#!/usr/bin/env python # -*- encoding=utf8 -*- # # Author 2011 Hsin-Yi Chen """Command-line tool adapter library This module is a command-line adapter library that: - transform arguments and options of command-line tool to Python arguments and keyword arguments. - provide a way to execute command-line tools in Python by OO way. Here is a example that execute `ls -al` in current directory ls = ucltip.Cmd('ls') ls(l=True) and the following is a simple usage example that launching a Zenity info dialog in Python zenity = ucltip.CmdDispatcher('zenity') zenity.subcmd_prefix = '--' zenity.conf.opt_style = 'gnu' zenity.info(text="The first example", width=500) The module contains the following public classes: - Cmd -- Object for mapping a command has no sub commands - CmdDispatcher -- Object for mapping a command has sub commands """ __all__ = ['global_config', 'regcmds', 'make_optargs', 'cmdexists', 'Cmd', 'SubCmd', 'CmdDispatcher', 'CommandNotFound', 'CommandExecutedError', 'RequireParentCmd', 'Pipe'] # global variabl, please use global_config function to access it # execmode: # process - run as process # list - produce command arguments list # string - produce command string # __GLOBAL_CONFIGS__ = {'execmode':'process', 'via_shell': False, 'dry_run':False, 'debug':False} # commands has sub command list # which is used in regcmds function # for selecting right class __CMDDISPATCHERS_LIST__ = ( 'apt-get', 'apt-cache', 'pbuilder', 'cowbuilder', 'bzr', 'git') import subprocess import syslog import sys import os extra = {} if sys.platform == 'win32': extra = {'shell': True} # ===================== # Logging functions # ===================== def ERR(cmdstr, errmsg='None'): syslog.syslog(syslog.LOG_ERR, 'UCLTIP: Executed "{}" failed, Err Msg:{}'.format(cmdstr, errmsg)) def DBG(msg): if global_config().get('debug'): syslog.syslog(syslog.LOG_DEBUG, 'UCLTIP: {} '.format(msg)) # ============================= # Utility functions and classes # ============================= def regcmds(*args, **kwargs): """register bound object in current environment @param cls Cmd or CmdDispatcher """ import __builtin__ cls = kwargs.get('cls') or Cmd assert cls in (Cmd, CmdDispatcher), 'cls should be Cmd or CmdDispatcher class' for cmdname in args: if cmdname in __CMDDISPATCHERS_LIST__: cls = CmdDispatcher __builtin__.__dict__[undashify(cmdname)] = cls(cmdname) def double_dashify(string): """add double dashify prefix in a string """ return '--' + string def dashify(string): """covert _ to - of string """ return string.replace('_', '-') def undashify(string): """covert - to _ of string """ return string.replace('-', '_') def cmdexists(cmdname): """check if command exists @param str cmdname command name @return bool True if command exists otherwise False """ assert 'PATH' in os.environ executable = lambda filename: os.path.isfile(filename) and os.access(filename, os.X_OK) filenames = [ os.path.join(element, str(cmdname)) \ for element in os.environ['PATH'].split(os.pathsep) if element ] for f in filenames: if executable(f): return True def global_config(query=None, **kwargs): """set or get global configure @param execmode: config executing mode avliabl value: process - run as process list - produce command arguments list string - produce command string @param dry_run: same as execmode=list @param debug: enable debug mode @return dict __GLOBAL_CONFIGS__ @example: # get value >>> global_config('dry_run') # set value >>> global_config(dry_run=True) """ if kwargs: __GLOBAL_CONFIGS__.update(kwargs) elif query: return __GLOBAL_CONFIGS__.get(query) else: return __GLOBAL_CONFIGS__ # ===================== # Options and Arguments # ===================== class OptionCreator(object): """Object for creating command options string from Python keyword arguments Support options style: - POSIX like options (ie. tar -zxvf foo.tar.gz) - GNU like long options (ie. du --human-readable --max-depth=1) - Java like properties (ie. java -Djava.awt.headless=true -Djava.net.useSystemProxies=true Foo) p.s Java like is not fully supported. Unsupport options style: - Short options with value attached (ie. gcc -O2 foo.c) - long options with single hyphen (ie. ant -projecthelp) """ """Support Option Styles, posix is default""" VALIDE_OPTSTYLES = ('posix', 'gnu', 'java') def __init__(self, opt_style='posix'): self._result = [] self.set_opt_style(opt_style) def set_opt_style(self, opt_style): self.opt_style = str(opt_style).lower() if not self.opt_style in self.VALIDE_OPTSTYLES: raise NotValideOptStyle(opt_style) def make_optargs(self, optname, values): """create command line options, same key but different values @param str optname @param list values @return list combined option args """ self._result = [] for v in values: self.__append_opt(optname, v) return self._result def transform_kwargs(self, **kwargs): """ Transforms Python style kwargs into command line options. @return list args for subprocess.call """ self._result = [] for k, v in kwargs.items(): self.__append_opt(k, v) DBG('Trasform Kwargs:input:{}, result:{}'.format(kwargs, self._result)) return self._result def __append_opt(self, k, v): """append option value transformed from kwargs to inputed args list @param str k option name @param str v option value """ if type(v) is not bool: v=str(v) if self.opt_style in ('gnu', 'java'): self._result.append('{0}={1}'.format(self.optname(k),v)) else: self._result.append(self.optname(k)) self._result.append(v) elif v == True: self._result.append(self.optname(k)) def optname(self, k): """get option name""" return (len(k) == 1 or self.opt_style == 'java') and \ '-{0}'.format(dashify(k)) or \ '--{0}'.format(dashify(k)) def make_optargs(optname, values, opt_style='posix'): """create command line options, same key but different values @param str optname @param list values @param int option style @return list combined option args """ return OptionCreator(opt_style).make_optargs(optname, values) # ===================== # Exceptions Clasees # ===================== class CommandNotFound(Exception): def __init__(self, cmd): self.cmd = cmd self.errmsg = 'command {} not found'.format(self.cmd) def __str__(self): return self.errmsg class CommandExecutedError(Exception): def __init__(self, status, errmsg=None): self.status = status self.errmsg = errmsg def __str__(self): return self.errmsg class RequireParentCmd(Exception): pass class NotValideOptStyle(Exception): def __init__(self, opt_style): self.opt_style = opt_style def __str__(self): return self.opt_style # ======================= # Command Adpater Classes # ======================= class CmdConfiguration(object): """Object for sharing common configurations """ def __init__(self): self.dry_run = global_config('dry_run') self.default_opts = {} self.opt_style = 'posix' class BaseCmd(object): def __init__(self, name=None): self.name = name or self.__class__.__name__.lower() self.conf = CmdConfiguration() DBG("Created a {}".format(repr(self))) @property def opt_style(self): return self.conf.opt_style @opt_style.setter def opt_style(self, value): self.conf.opt_style = value def opts(self, **kwargs): """set default options of command @param dict kwargs options dict @return dict default options if no kwargs input @example obj.opts(t=3) # the result is {'t':3} obj.opts() """ return kwargs and self.conf.default_opts.update(kwargs) or self.conf.default_opts def reset(self): """reset default options""" self.conf.default_opts = {} class ExecutableCmd(BaseCmd): execute_kwargs = ('stdin','as_process', 'via_shell', 'with_extend_output', 'cwd') def __call__(self, *args, **kwargs): return self._callProcess(*args, **kwargs) def _callProcess(self, *args, **in_kwargs): # Handle optional arguments prior to calling transform_kwargs # otherwise these'll end up in args, which is bad. kwargs = {} kwargs.update(self.opts()) kwargs.update(in_kwargs) _kwargs = {} for kwarg in self.execute_kwargs: try: _kwargs[kwarg] = kwargs.pop(kwarg) except KeyError: pass # Prepare the argument list call = self.make_callargs(*args, **kwargs) DBG('Builded command string:{}'.format(call)) # Execute mode = global_config('execmode') if self.conf.dry_run or mode == 'list': return call if mode == 'string': return ' '.join(call) return self.execute(call, **_kwargs) def execute(self, command, stdin=None, as_process=False, via_shell=False, with_extend_output=False, cwd=None): """execute command @param subprocess.PIPE stdin @param bool as_process retrun Popen instance if as_process is True for more control @param bool via_shell use os.system instead of subprocess.call @param str cwd If cwd is not None, the current directory will be changed to cwd before the child is executed @return str execited result (as_process musc be False) @example # the same as echo `ls -al|grep Dox` ls = ucltip.Cmd('ls') grep = ucltip.Cmd('grep') print grep('Dox', stdin=ls(a=True, l=True, as_process=True).stdout) """ assert not (as_process and via_shell),\ "You can not get a Popen instance when you want to execute command in shell." assert not (stdin and via_shell),\ "You can not use stdin and via_shell in the same time." if global_config('via_shell') or via_shell: status = os.system(' '.join(command)) if status != 0: ERR(' '.join(command)) raise CommandExecutedError(status) return status else: # Start the process proc = subprocess.Popen(command, stdin=stdin, stderr=subprocess.PIPE, stdout=subprocess.PIPE, cwd=cwd, **extra ) if as_process: return proc # Wait for the process to return try: stdout_value = proc.stdout.read() stderr_value = proc.stderr.read() status = proc.wait() finally: proc.stdout.close() proc.stderr.close() if not with_extend_output: if status != 0: ERR(' '.join(command), stderr_value) raise CommandExecutedError(status, stderr_value) return stdout_value else: return (status, stdout_value) def make_callargs(self, *args, **kwargs): # Prepare the argument list opt_args = OptionCreator(self.conf.opt_style).transform_kwargs(**kwargs) ext_args = map(str, args) args = ext_args + opt_args return [self.name] + args def __repr__(self): opt = self.opts() and ' ' + " ".join(OptionCreator(self.conf.opt_style).transform_kwargs(**self.opts())) or '' return "{0} object bound '{1}{2}'".format(self.__class__.__name__, self.name, opt) class Cmd(ExecutableCmd): """Object for mapping a command has no sub commands Keyword Arguments: - name -- A string indicating the command name will be executed - opt_style -- A string to indicate what options style be used , avaliable values are `posix`, `gnu`, `java`, the default is posix """ def __init__(self, name=None): super(Cmd, self).__init__(name) if not cmdexists(self.name): raise CommandNotFound(self.name) class SubCmd(ExecutableCmd): """Object for mapping a sub command, this object can not be executed without a CmdDispatcher parent object. Keyword Arguments: - name -- A string indicating the sub command name will be executed - parent -- A CmdDispatcher provides main command name, default opt_style, options, and subcmd_prefix. - opt_style -- delegate to Parent Command opt_style (read only) """ def __init__(self, name, parent=None): super(SubCmd, self).__init__(name) self.parent = parent # data delegations if parent: self.set_parent(parent) def set_parent(self, parent): """set parent and the common configuration will be override by the parent @param CmdDispatcher """ self.conf = self.parent.conf def make_callargs(self, *args, **kwargs): if not self.parent: raise RequireParentCmd if self.parent.subcmd_prefix and not self.parent.subcmd_prefix in self.name: self.name = self.parent.subcmd_prefix + self.name args = super(SubCmd, self).make_callargs(*args, **kwargs) args.insert(0, self.parent.name) return args class CmdDispatcher(BaseCmd): """Object for mapping a command has sub commands Keyword Arguments: - name -- A string indicating the sub command name will be executed - subcmd_prefix -- A string indicating prefix string of a sub command if required - opt_style - A string to indicate what options style be used , avaliable values are `posix`, `gnu`, `java`, the default is posix """ def __init__(self, name=None): self.subcmd_prefix = None self._subcmds = {} super(CmdDispatcher, self).__init__(name) if not cmdexists(self.name): raise CommandNotFound(self.name) def __getattr__(self, name): if name[:1] == '_': raise AttributeError(name) return self.getsubcmd(name) def getsubcmd(self, name): return self._subcmds.setdefault(name, SubCmd(name, self)) def __repr__(self): return "{0} object bound '{1}'".format(self.__class__.__name__, self.name) # ============ # Pipe Classes # ============ class PipeError(Exception): pass class Pipe(object): """Object for handling command pipeline """ def __init__(self): # last process self._last_proc = None def add(self, cmd, *args, **opts): """add command arguments in this pipe @param cmd command name @param args command arguments @param opts command options """ if type(cmd) is str and not opts: cmd = Cmd(cmd) opts['as_process'] = True if self._last_proc: opts['stdin'] = self._last_proc.stdout self._last_proc = cmd(*args, **opts) def wait(self): """Wait for the process to terminate. Returns returncode attribute. """ if not self._last_proc: raise PipeError("theres is no any process inside") status = self._last_proc.wait() if status != 0: raise PipeError() return status def __getattr__(self, k): if self._last_proc and k in ('status', 'stdout', 'stderr'): try: return getattr(self._last_proc, k) except AttributeError: return None ucltip-0.7.1/PKG-INFO0000664000175000017500000000204511674112161014675 0ustar hychenhychen00000000000000Metadata-Version: 1.0 Name: ucltip Version: 0.7.1 Summary: A library to help making command line tool Python binding faster Home-page: http://github.com/hychen/ucltip Author: Hsin-Yi Chen 陳信屹 (hychen) Author-email: ossug.hychen@gmail.com License: BSD-2-clause License Description: This library makes you to use command line tool in Python by OO way. The concept is to transform 1) command as a instance, 2) options of command as arguments and keyword arguments of function or instance method when method be used as a sub command of a command. Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Topic :: Software Development :: Libraries :: Python Modules ucltip-0.7.1/README.txt0000664000175000017500000002152011674111601015273 0ustar hychenhychen00000000000000# vim:syn=rst: ======================================== UCLTIP - Use command line tool in Python ======================================== This library makes you use command line tool in Python more easier. The original idea are from GitPython project `http://pypi.python.org/pypi/GitPython/` The basic concept is to transform: 1) command as a instance, 2) command options as arguments or function/method keyword arguments. Feature: - Transform CLI Tool arguments to Python function/method arguments - Transform CLI Tool Boolean option style to Python function/method keyword arguments - Transform CLI Tool Key-Value option style to Python function/method keyword arguments - Transform CLI Tool Key-Value option style to Python function/method keyword arguments - Transform CLI Command as Python Class and use it in your script - Set default options of Command for multiple use ------- Example ------- :: >>>expr = ucltip.Cmd('expr') >>>ret = expr(3, '+', 4) >>>ret "7\n" create a Cmd callable object :: >>>expr = ucltip.Cmd('expr') run Cmd object as a function to get result like executing `expr 3 + 4` in shell, the result will be storeged in '''ret''' variable, as the following shows, you can think command line tool arguments as Python function arguments. :: >>>ret = expr(3, '+', 4) if you want to only check what command string will be combined, you can doing dry run, but ramind the command string will be split as a list. :: >>>expr = ucltip.Cmd('expr') >>>expr.conf.dry_run = True >>>expr(3, "+", 4) ['expr', '3', '+', '4'] please note that jhe command be executed by subprocess.call, it bypass the shell. :: # the result is $HOME, and it will not show output directly print Cmd('echo')("$HOME") if you want execute command via shell and use shell enviroment variable, please do as follow, if args of function includes '''via_shell=True''', the command be executed by os.system :: # the result is "/home/somebody", and show output directly Cmd('echo')("$HOME", via_shell=True) ----------------------------------- Handling Error of command execution ----------------------------------- if the command you want to use is not exists, the exception ucltip.CommandNotFound raises :: >> a=ucltip.Cmd('oo') Traceback (most recent call last): File "", line 1, in File "ucltip/__init__.py", line 103, in __init__ raise CommandNotFound() ucltip.CommandNotFound if the command be executed falied, the exception ucltip.CommandExecutedError raises :: >>> a=ucltip.Cmd('ls') >>> a Cmd object bound 'ls' >>> a(ccc=True) Traceback (most recent call last): File "", line 1, in File "ucltip/__init__.py", line 109, in __call__ return self._callProcess(*args, **kwargs) File "ucltip/__init__.py", line 126, in _callProcess return self.execute(call, **_kwargs) File "ucltip/__init__.py", line 169, in execute raise CommandExecutedError(status, stderr_value) ucltip.CommandExecutedError: ls: unrecognized option '--ccc' Try `ls --help' for more information. here is a example to hanlde error: :: try: print ucltip.Cmd('ls') except ucltip.CommandExecutedError as e: print e -------------- Command Option -------------- so far, we already leanr how to execute command with arguments, but how about command options? it is pretty easy also, just think command option like python keyword arguments. ''Boolean option'' when the type of keyword arguments's value is boolean, then this kind of keyword arguments will be converted to command boolean option, for example, `-a` is equal '''func(a=True)''' :: >>>ls('/tmp', a=True) ['ls', '/tmp', '-a'] Key-Value option ================ when the type of keyword arguments's value is interge number or string, then these of keyword arguments will be coverted to command key-value option, for example, '--quoting_style 1' is equal '''func(quoting_style=1)''' :: >>>ls('tmp', quoting_style='c') ['ls', '--quoting-style', 'c'] also, you can change option style by set '''opt_style''' attribute, support option style are `gnu`, `posix`,`java`, the default value is `posix`. note: java option style is not fully supported. :: >>>ls.conf.opt_style = 'gnu' >>>ls('tmp', quoting_style='c') ['ls', '--quoting-style=c'] some options is mutiple, which means the name is name, but you can give many different values, for example :: # `foo -a -b -o Dir=/var -o Dir::Cache=/tmp` # so you need to use make_optargs to create args if the opt name is duplicate optargs = ucltip.make_optargs('o', ('Dir=/var','Dir::Cache=/tmp')) Cmd('foo')(optargs, a=True, b=True) ------------- CmdDispatcher ------------- The CmdDispatcher is an object for mapping some command tools has sub command, like `git`, `zenity`, `pbuilder`, `apt-get`, etc. method name indicates as sub command name, method arguements indicates sub command arguments, and method keyword arguments indicates sub command options. :: >>apt_get = ucltip.CmdDispatcher('apt-get') >>apt_get.conf.dry_run = True >>apt_get.install('vim') ['apt-get', 'install', 'vim'] if sub command has prefix string, you can use '''subcmd_prefix''' attribute to set it. :: >>zenity = ucltip.CmdDispatcher('zenity') >>zenity.subcmd_prefix = '--' >>zenity.conf.dry_run = True >>zenity.info(text='hello') ['zenity', '--info', '--text hello'] -------------- Default Option -------------- the options does not be stored after you execute command, if you want to keep options for multiple using, you can use ''''opts''' function to set default options as the following :: >>>ls = ucltip.Cmd('ls) >>>ls.conf.dry_run=True >>>ls.opts(l=True) >>>ls('/tmp') ['ls', '/tmp', '-l'] CmdDispatcher sub command will load default options from its parent, in this case, '''apt_get.install.opts()''' is the same as '''apt_get.opts()''' :: >>>apt_get = ucltip.CmdDispatcher('apt-get') >>>apt_get.conf.dry_run = True >>>apt_get.opts(t='maverick') >>>apt_get.install.opts() {t':'maverick'} >>>apt_get.install('vim') apt-get', 'install', 'vim', '-t maverick'] >>>apt_get.opts(t=False) >>>apt_get.install('vim') ['apt-get', 'install', 'vim'] Pipe ==== In subprocess, the way for doing pipeline is :: >>>p1 = Popen(["dmesg"], stdout=PIPE) >>>p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) >>>output = p2.communicate()[0] which is not convenience when you want to pipe many commands. Pipe class provide similar interface as C library `libpipeline` (http://libpipeline.nongnu.org/) that manipulating pipelines of subprocesses in a flexible and convenient way in C. firstly, you need to create a Pipe instance :: >>>pipe = ucltip.Pipe() and then add some command arguments as you want :: >>>pipe.add('expr', 1, '+' 3) >>>pipe.add('sed', 's/4/5/', '--posix') finally run the process, wait it terminate and get its output :: >>>pipe.wait() >>>pipe.stdout.read() 5 # get error message in case command executed error >>>pipe.stderr.read() the first argument of Pipe.add function can be Cmd or SubCmd, please remaind the usage of add function is changed in this case :: >>>pipe = ucltip.Pipe() >>>pipe.add(Cmd('expr'), 1, '+', 3) >>>pipe.add(Cmd('sed'), 's/4/5', posix=True) >>>pipe.wait() >>>pipe.stdout.read() 5 :: >>>apt_cache = ucltip.CmdDispatcher('apt-cache') >>>pipe = ucltip.Pipe() >>>pipe.add(apt_cache.search, 'vim-common') >>>pipe.add(Cmd('grep'), 'vim') >>>pipe.wait() >>>pipe.stdout.read() Helper ====== regcmds is used to register multiple command in built-in environment one time :: >>>ucltip.regcmds('ls', 'wget', 'sed') >>> ls Cmd object bound 'ls' >>> wget Cmd object bound 'wget' >>> sed Cmd object bound 'sed' avaliabl for specify class :: >>>ucltip.regcmds('apt-get', 'apt-cache', cls=ucltip.CmdDispatcher) >>> apt_get >>> apt_cache ucltip can also check the command name you want to register is in the list that command has sub command or not for reduce the class to CmdDispatcher by auto. the list is ucltip.__CMDDISPATCHERS_LIST__, request to add new command are welcome `global_config` is used to set up global configure of All class To change executing behavior of Cmd or CmdDispatcher :: # executing command, default setting >>>ucltip.global_config(execmod='process') # produce command arguments only, same as dry_run >>>ucltip.global_config(execmod='list') >>>ucltip.Cmd('ls')(a=True) ['ls', '-a'] # produce command string only >>>ucltip.global_config(execmod='string') >>>ucltip.Cmd('ls')(a=True) 'ls -a' To force all command executed by os.system :: >>>ucltip.global_config(via_shell=True) Debugging ========= ucltip provid debug output in /var/log/syslog after you enable debug mode :: >>> ucltip.global_config(debug=True) Get invlolved ============= if you are interesting to help, please contact author, Hychen, his email is . The VCS of code is avaliabl on http://github.com/hychen/ucltip ucltip-0.7.1/setup.py0000775000175000017500000000230711674103616015323 0ustar hychenhychen00000000000000#!/usr/bin/env python # -*- encoding=utf8 -*- # # Copyright © 2011 Hsin Yi Chen from distutils.core import setup setup( name = 'ucltip', version = open('VERSION.txt').read().strip(), description = 'A library to help making command line tool Python binding faster', long_description=""" This library makes you to use command line tool in Python by OO way. The concept is to transform 1) command as a instance, 2) options of command as arguments and keyword arguments of function or instance method when method be used as a sub command of a command. """, author = 'Hsin-Yi Chen 陳信屹 (hychen)', author_email = 'ossug.hychen@gmail.com', url='http://github.com/hychen/ucltip', license = 'BSD-2-clause License', packages=['ucltip'], classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.5", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Topic :: Software Development :: Libraries :: Python Modules", ] ) ucltip-0.7.1/VERSION.txt0000664000175000017500000000000611674112145015463 0ustar hychenhychen000000000000000.7.1 ucltip-0.7.1/MANIFEST.in0000664000175000017500000000004611674112145015337 0ustar hychenhychen00000000000000include * exclude .gitignore MANIFEST ucltip-0.7.1/__init__.py0000644000175000017500000000000011572642421015700 0ustar hychenhychen00000000000000ucltip-0.7.1/test/0000775000175000017500000000000011674112161014556 5ustar hychenhychen00000000000000ucltip-0.7.1/test/test_all.py0000664000175000017500000003073111674111601016741 0ustar hychenhychen00000000000000#!/usr/bin/env python # -*- encoding=utf8 -*- # # Author 2011 Hsin-Yi Chen import os import unittest import ucltip # setup test env def setup_testenv(): testbinpath = os.path.join(os.path.dirname(os.path.abspath(__file__)),'bin') os.environ['PATH'] = "{0}:{1}".format(os.getenv('PATH'), testbinpath) class UtilsTestCase(unittest.TestCase): def setUp(self): self.optcreator = ucltip.OptionCreator() def test_kwargsname_to_optionname(self): """test transform keyword arguments's name to command's option name. """ # POSIX and GNU Style for style in ('posix', 'gnu', 'POSIX', 'GNU'): self.optcreator.set_opt_style(style) self.assertEquals(self.optcreator.optname('k'), '-k') self.assertEquals(self.optcreator.optname('key'), '--key') self.assertEquals(self.optcreator.optname('key_one'), '--key-one') # Java Style self.optcreator.set_opt_style('java') self.assertEquals(self.optcreator.optname('k'), '-k') self.assertEquals(self.optcreator.optname('key'), '-key') self.assertEquals(self.optcreator.optname('key_one'), '-key-one') def test_transform_kwargs_to_booloption(self): """test transform keyword to command's boolean style option. """ # POSIX and GNU for style in ('posix', 'gnu'): self.optcreator.set_opt_style(style) self.assertEquals(self.optcreator.transform_kwargs(k=True), ['-k']) self.assertEquals(self.optcreator.transform_kwargs(key=True), ['--key']) # Java Style self.optcreator.set_opt_style('java') self.assertEquals(self.optcreator.transform_kwargs(key=True), ['-key']) def test_transform_kwargs_to_keyvauleoption(self): """test transform keyword to command's key-value style option name. """ self.optcreator.set_opt_style('posix') self.assertEquals(self.optcreator.transform_kwargs(k=123), ['-k', '123']) self.assertEquals(self.optcreator.transform_kwargs(key=123), ['--key', '123']) self.optcreator.set_opt_style('gnu') self.assertEquals(self.optcreator.transform_kwargs(k=123), ['-k=123']) self.assertEquals(self.optcreator.transform_kwargs(key=123), ['--key=123']) self.optcreator.set_opt_style('java') self.assertEquals(self.optcreator.transform_kwargs(k=123), ['-k=123']) self.assertEquals(self.optcreator.transform_kwargs(key=123), ['-key=123']) def test_make_multi_options(self): """test make multi option string with the same option name """ self.assertEquals(ucltip.make_optargs('colum', ('first','second'), 'posix'), ['--colum','first', '--colum', 'second']) self.assertEquals(ucltip.make_optargs('colum', ('first','second'), 'gnu'), ['--colum=first','--colum=second']) self.assertEquals(ucltip.make_optargs('colum', ('first','second'), 'java'), ['-colum=first','-colum=second']) # exception check self.assertRaises(ucltip.NotValideOptStyle, ucltip.make_optargs, 'colum', ('first','second'), 0) self.assertRaises(ucltip.NotValideOptStyle, ucltip.make_optargs, 'colum', ('first','second'), 1) def test_cmdexist(self): """check commands exists """ self.assertFalse(ucltip.cmdexists(None)) self.assertFalse(ucltip.cmdexists('')) self.assertFalse(ucltip.cmdexists(1234.5)) self.assertFalse(ucltip.cmdexists(1234)) def test_command_not_found(self): """raise Exception if commands does not exist""" self.assertRaises(ucltip.CommandNotFound, ucltip.Cmd, None) self.assertRaises(ucltip.CommandNotFound, ucltip.Cmd, '') self.assertRaises(ucltip.CommandNotFound, ucltip.Cmd, 1234.5) self.assertRaises(ucltip.CommandNotFound, ucltip.Cmd, '000') class ExecuteCmdTestCase(unittest.TestCase): def setUp(self): self.expr = ucltip.Cmd('expr') self.sed = ucltip.Cmd('sed') def test_call(self): self.assertEquals(self.expr('3', '+', '4'), '7\n') self.assertRaises(ucltip.CommandExecutedError, self.expr, '3', '5', '4') self.assertEquals(self.expr('3', '+', '4', via_shell=True), 0) def test_pipe(self): """test command pipe line""" first_cmd = self.expr('3','+','4', as_process=True) second_cmd = self.sed self.assertEquals('A\n', second_cmd('s/7/A/', stdin=first_cmd.stdout)) def test_opt(self): """test default options setting""" self.assertRaises(TypeError, self.expr.opts, 1) self.expr.opts(opt1=1,opt2=2) self.assertEquals({'opt1': 1, 'opt2': 2}, self.expr.opts()) self.expr.reset() self.assertEquals({}, self.expr.opts()) def test_dry_run(self): """test dry_run """ self.expr.conf.dry_run = True self.assertEquals(['expr', '1', '+', '2'], self.expr(1, '+', 2)) def test_repr(self): self.assertEquals("Cmd object bound 'expr'", "{0}".format(self.expr)) self.expr.opts(a=True) self.assertEquals("Cmd object bound 'expr -a'", "{0}".format(self.expr)) class SubCmdTestCase(unittest.TestCase): def setUp(self): self.parent = ucltip.CmdDispatcher('ucltip-apt-get') self.subcmd = ucltip.SubCmd('mock-cmd') self.psubcmd = ucltip.SubCmd('install', self.parent) def test_noparent_call(self): self.assertRaises(ucltip.RequireParentCmd, self.subcmd) def test_hasparent_call(self): """test executoing sub command which has parent command""" self.assertEquals('ucltip-apt-get install vim\n', self.psubcmd('vim')) self.assertEquals('ucltip-apt-get install vim -b\n', self.psubcmd('vim', b=True)) self.assertEquals('ucltip-apt-get install vim -t maverick\n', self.psubcmd('vim', t='maverick')) self.assertEquals('ucltip-apt-get install vim --test maverick\n', self.psubcmd('vim', test='maverick')) # check another option style self.parent.opt_style = 'gnu' self.assertEquals('ucltip-apt-get install vim -t=maverick\n', self.psubcmd('vim', t='maverick')) self.assertEquals('ucltip-apt-get install vim --test=maverick\n', self.psubcmd('vim', test='maverick')) def test_hasparent_opts(self): self.parent.opts(def_opt=True, def_opt2=1) self.assertEquals({'def_opt':True, 'def_opt2':1}, self.psubcmd.parent.opts()) self.assertEquals({'def_opt':True, 'def_opt2':1}, self.psubcmd.opts()) class CmdDispatcherTestCase(unittest.TestCase): def setUp(self): self.cmdd = ucltip.CmdDispatcher('ucltip-apt-get') def test_callsubcmd(self): """test call subcmd by cmd dispatcher""" self.assertEquals('ucltip-apt-get install vim\n', self.cmdd.install('vim')) self.assertEquals('ucltip-apt-get install vim -t maverick\n', self.cmdd.install('vim', t='maverick')) self.assertEquals('ucltip-apt-get install vim --test maverick\n', self.cmdd.install('vim', test='maverick')) # check another option style self.cmdd.opt_style = 'gnu' self.assertEquals('ucltip-apt-get install vim -t=maverick\n', self.cmdd.install('vim', t='maverick')) self.assertEquals('ucltip-apt-get install vim --test=maverick\n', self.cmdd.install('vim', test='maverick')) # check another sub command prefix self.cmdd.subcmd_prefix = '--' self.assertEquals('ucltip-apt-get --install vim -t=maverick\n', self.cmdd.install('vim', t='maverick')) def test_opts(self): """test setting default options of cmd dispatcher""" self.cmdd.opts(def_opt=1) self.assertEquals('ucltip-apt-get install vim --def-opt 1 -t maverick\n', self.cmdd.install('vim', t='maverick')) self.cmdd.opts(def_opt=False) self.assertEquals('ucltip-apt-get install vim -t maverick\n', self.cmdd.install('vim', t='maverick')) def test_subcmd_prefix(self): """test setting subcmd_prefix of cmd dispatcher""" self.cmdd.subcmd_prefix = '--' self.assertEquals('ucltip-apt-get --install\n', self.cmdd.install()) def test_opt_style(self): """test setting option style of cmd dispatcher""" self.cmdd.conf.opt_style = 'gnu' self.assertEquals('ucltip-apt-get install --test=1\n', self.cmdd.install(test=1)) self.cmdd.conf.opt_style = 'java' self.assertEquals('ucltip-apt-get install -test=1\n', self.cmdd.install(test=1)) class CustomClassTestCase(unittest.TestCase): def test_cmd(self): class LS(ucltip.Cmd): pass self.assertEquals(LS().name, 'ls') def test_cmdd(self): class Zenity(ucltip.CmdDispatcher): def __init__(self): super(Zenity, self).__init__() self.subcmd_prefix = '--' self.conf.opt_style = 'gnu' self.conf.dry_run = True self.error = lambda *args, **kwargs: self.call('error', *args, **kwargs) def info(self, *args, **kwargs): kwargs['text']='hi' return self.getsubcmd('info')(*args, **kwargs) def call(self, name, *args, **kwargs): return args, kwargs self.assertEquals(['zenity', '--info', '--text=hi'], Zenity().info()) self.assertEquals(((1,2,3), {'a':1}), Zenity().error(1,2,3, a=1)) class PipeTestCase(unittest.TestCase): def setUp(self): self.pipe = ucltip.Pipe() def tearDown(self): del self.pipe def test_cmd_obj_param(self): self.pipe.add(ucltip.Cmd('expr'), 1, '+', 3) self.pipe.add(ucltip.Cmd('sed'), 's/4/8/', posix=True) self.pipe.wait() self.assertEquals('8\n', self.pipe.stdout.read()) def test_cmdd_obj_param(self): self.pipe.add(ucltip.CmdDispatcher('apt-cache').search, 'vim-common', q=True) self.pipe.add(ucltip.Cmd('awk'), '{ print $1 }') self.pipe.wait() self.assertEquals('vim-common\n', self.pipe.stdout.read()) def test_cmd_str_param(self): self.pipe.add('expr', 1, '+', 3) self.pipe.add('sed', 's/4/8/', '--posix') self.pipe.wait() self.assertEquals('8\n', self.pipe.stdout.read()) def test_exception(self): self.assertRaises(ucltip.PipeError, self.pipe.wait) class HelperTestCase(unittest.TestCase): def setUp(self): self._cmds = [] def tearDown(self): import __builtin__ for varname in self._cmds: del __builtin__.__dict__[varname] def _regcmds(self, *args, **kwargs): for cmd in args: if cmd not in self._cmds: self._cmds.append(ucltip.undashify(cmd)) ucltip.regcmds(*args, **kwargs) def test_call(self): self._regcmds('ls', 'sed') self.assertEquals(type(ls), ucltip.Cmd) self.assertEquals(type(sed), ucltip.Cmd) self._regcmds('apt-get', 'apt-cache', cls=ucltip.CmdDispatcher) self.assertEquals(type(apt_get), ucltip.CmdDispatcher) self.assertEquals(type(apt_cache), ucltip.CmdDispatcher) self.assertRaises(AssertionError, self._regcmds, 'ls', cls=type) def test_regcmddispatcher(self): self._regcmds('apt-get') self.assertEquals(type(apt_get), ucltip.CmdDispatcher) class GlobalConfigTestCase(unittest.TestCase): def setUp(self): self.default_config = ucltip.global_config() def tearDown(self): ucltip.__GLOBAL_CONFIG__ = self.default_config self.assertEquals(self.default_config, ucltip.global_config()) def test_execmode_list(self): ucltip.global_config(execmode='list') self.assertEquals(['ls','-a','-l'], ucltip.Cmd('ls')(a=True, l=True)) def test_execmode_string(self): ucltip.global_config(execmode='string') self.assertEquals('apt-get install vim -t maverick', ucltip.CmdDispatcher('apt-get').install('vim',t='maverick')) def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(UtilsTestCase, 'test')) suite.addTest(unittest.makeSuite(ExecuteCmdTestCase, 'test')) suite.addTest(unittest.makeSuite(SubCmdTestCase, 'test')) suite.addTest(unittest.makeSuite(CmdDispatcherTestCase, 'test')) suite.addTest(unittest.makeSuite(CustomClassTestCase, 'test')) suite.addTest(unittest.makeSuite(PipeTestCase, 'test')) suite.addTest(unittest.makeSuite(HelperTestCase, 'test')) suite.addTest(unittest.makeSuite(GlobalConfigTestCase, 'test')) return suite if __name__ == '__main__': setup_testenv() unittest.main(defaultTest='suite')