diff --git a/pyqi/commands/code_header_generator.py b/pyqi/commands/code_header_generator.py
index c3808a4..caf71e9 100644
--- a/pyqi/commands/code_header_generator.py
+++ b/pyqi/commands/code_header_generator.py
@@ -11,8 +11,9 @@
__credits__ = ["Jai Ram Rideout", "Daniel McDonald"]
-from pyqi.core.command import (Command, CommandIn, CommandOut,
- ParameterCollection)
+from pyqi.core.command import (Command, CommandIn, CommandOut,
+ ParameterCollection)
+
class CodeHeaderGenerator(Command):
BriefDescription = "Generate header code for use in a Python file"
diff --git a/pyqi/commands/make_bash_completion.py b/pyqi/commands/make_bash_completion.py
index f82b8a7..29d5f71 100644
--- a/pyqi/commands/make_bash_completion.py
+++ b/pyqi/commands/make_bash_completion.py
@@ -11,19 +11,21 @@
from __future__ import division
__credits__ = ["Daniel McDonald", "Jai Ram Rideout", "Doug Wendel",
- "Greg Caporaso"]
+ "Greg Caporaso"]
import importlib
-from pyqi.core.command import (Command, CommandIn, CommandOut,
- ParameterCollection)
+from pyqi.core.command import (Command, CommandIn, CommandOut,
+ ParameterCollection)
from pyqi.core.interface import get_command_names, get_command_config
+
def _get_cfg_module(desc):
"""Load a module"""
mod = importlib.import_module(desc)
return mod
-# Based on http://stackoverflow.com/questions/5302650/multi-level-bash-completion
+# Based on
+# http://stackoverflow.com/questions/5302650/multi-level-bash-completion
script_fmt = """_%(driver)s_complete()
{
local cur prev
@@ -34,7 +36,7 @@ def _get_cfg_module(desc):
if [ $COMP_CWORD -gt 1 ]; then
prev=${COMP_WORDS[1]}
- fi
+ fi
if [ $COMP_CWORD -eq 1 ]; then
COMPREPLY=( $(compgen -W "%(command_list)s" -- $cur) )
@@ -56,11 +58,12 @@ def _get_cfg_module(desc):
;;
"""
+
class BashCompletion(Command):
BriefDescription = "Construct a bash completion script"
- LongDescription = ("Construct a bash tab completion script that will search"
- " through available commands and options")
-
+ LongDescription = ("Construct a bash tab completion script that will "
+ "search through available commands and options")
+
CommandIns = ParameterCollection([
CommandIn(Name='command_config_module', DataType=str,
Description="CLI command configuration module",
@@ -77,7 +80,6 @@ class BashCompletion(Command):
def run(self, **kwargs):
driver = kwargs['driver_name']
cfg_mod_path = kwargs['command_config_module']
- cfg_mod = _get_cfg_module(cfg_mod_path)
command_names = get_command_names(cfg_mod_path)
command_list = ' '.join(command_names)
@@ -89,14 +91,15 @@ def run(self, **kwargs):
if cmd_cfg is not None:
command_options = []
command_options.extend(
- sorted(['--%s' % p.Name for p in cmd_cfg.inputs]))
+ sorted(['--%s' % p.Name for p in cmd_cfg.inputs]))
opts = ' '.join(command_options)
- commands.append(command_fmt % {'command':cmd, 'options':opts})
+ commands.append(command_fmt %
+ {'command': cmd, 'options': opts})
all_commands = ''.join(commands)
- return {'result':script_fmt % {'driver':driver,
- 'commands':all_commands,
- 'command_list':command_list}}
+ return {'result': script_fmt % {'driver': driver,
+ 'commands': all_commands,
+ 'command_list': command_list}}
CommandConstructor = BashCompletion
diff --git a/pyqi/commands/make_command.py b/pyqi/commands/make_command.py
index 75a5489..35640e3 100644
--- a/pyqi/commands/make_command.py
+++ b/pyqi/commands/make_command.py
@@ -11,11 +11,10 @@
__credits__ = ["Daniel McDonald", "Greg Caporaso", "Doug Wendel",
"Jai Ram Rideout"]
-from pyqi.core.command import (Command, CommandIn, CommandOut,
- ParameterCollection)
+from pyqi.core.command import CommandIn, CommandOut, ParameterCollection
from pyqi.commands.code_header_generator import CodeHeaderGenerator
-command_imports = """from pyqi.core.command import (Command, CommandIn, CommandOut,
+command_imports = """from pyqi.core.command import (Command, CommandIn, CommandOut,
ParameterCollection)"""
command_format = """class %s(Command):
@@ -56,43 +55,44 @@ def test_run(self):
if __name__ == '__main__':
main()"""
+
class MakeCommand(CodeHeaderGenerator):
BriefDescription = "Construct a stubbed out Command object"
LongDescription = ("This command is intended to construct the basics of a "
- "Command object so that a developer can dive straight into the "
- "implementation of the command")
+ "Command object so that a developer can dive straight "
+ "into the implementation of the command")
CommandIns = ParameterCollection(
- CodeHeaderGenerator.CommandIns.Parameters + [
- CommandIn(Name='name', DataType=str,
- Description='the name of the Command', Required=True),
- CommandIn(Name='test_code', DataType=bool,
- Description='create stubbed out unit test code',
- Required=False, Default=False)
- ]
+ CodeHeaderGenerator.CommandIns.Parameters + [
+ CommandIn(Name='name', DataType=str,
+ Description='the name of the Command', Required=True),
+ CommandIn(Name='test_code', DataType=bool,
+ Description='create stubbed out unit test code',
+ Required=False, Default=False)
+ ]
)
CommandOuts = ParameterCollection([
- CommandOut(Name='result',DataType=list,
- Description='The resulting template')
- ]
+ CommandOut(Name='result', DataType=list,
+ Description='The resulting template')
+ ]
)
def run(self, **kwargs):
code_header_lines = super(MakeCommand, self).run(
- author=kwargs['author'], email=kwargs['email'],
- license=kwargs['license'], copyright=kwargs['copyright'],
- version=kwargs['version'], credits=kwargs['credits'])['result']
+ author=kwargs['author'], email=kwargs['email'],
+ license=kwargs['license'], copyright=kwargs['copyright'],
+ version=kwargs['version'], credits=kwargs['credits'])['result']
result_lines = code_header_lines
if kwargs['test_code']:
result_lines.extend(
- (test_format % {'name': kwargs['name']}).split('\n'))
+ (test_format % {'name': kwargs['name']}).split('\n'))
else:
result_lines.extend(command_imports.split('\n'))
result_lines.append('')
result_lines.extend((command_format % (
- kwargs['name'], kwargs['name'])).split('\n'))
+ kwargs['name'], kwargs['name'])).split('\n'))
return {'result': result_lines}
diff --git a/pyqi/commands/make_optparse.py b/pyqi/commands/make_optparse.py
index b99930a..7d89218 100644
--- a/pyqi/commands/make_optparse.py
+++ b/pyqi/commands/make_optparse.py
@@ -10,8 +10,8 @@
from __future__ import division
from operator import attrgetter
-from pyqi.core.command import (Command, CommandIn, CommandOut,
- ParameterCollection)
+from pyqi.core.command import (Command, CommandIn, CommandOut,
+ ParameterCollection)
from pyqi.commands.code_header_generator import CodeHeaderGenerator
__credits__ = ["Daniel McDonald", "Jai Ram Rideout", "Greg Caporaso",
@@ -81,7 +81,7 @@
# # value will be made available to Handler. This name
# # can be either an underscored or dashed version of the
# # option name (e.g., 'output_fp' or 'output-fp')
- # InputName='output-fp'),
+ # InputName='output-fp'),
#
# An example option that does not map to a CommandIn.
# OptparseResult(Parameter=cmd_out_lookup('some_other_result'),
@@ -109,16 +109,19 @@
default_block_format = """# Default=%(default)s, # implied by Parameter
# DefaultDescription=%(default_description)s, # implied by Parameter"""
+
class MakeOptparse(CodeHeaderGenerator):
BriefDescription = "Consume a Command, stub out an optparse configuration"
- LongDescription = """Construct and stub out the basic optparse configuration for a given Command. This template provides comments and examples of what to fill in."""
-
+ LongDescription = "Construct and stub out the basic optparse "\
+ "configuration for a given Command. This template "\
+ "provides comments and examples of what to fill in."
+
CommandIns = ParameterCollection(
CodeHeaderGenerator.CommandIns.Parameters + [
- CommandIn(Name='command', DataType=Command,
- Description='an existing Command', Required=True),
- CommandIn(Name='command_module', DataType=str,
- Description='the Command source module', Required=True)
+ CommandIn(Name='command', DataType=Command,
+ Description='an existing Command', Required=True),
+ CommandIn(Name='command_module', DataType=str,
+ Description='the Command source module', Required=True)
]
)
@@ -129,9 +132,9 @@ class MakeOptparse(CodeHeaderGenerator):
def run(self, **kwargs):
code_header_lines = super(MakeOptparse, self).run(
- author=kwargs['author'], email=kwargs['email'],
- license=kwargs['license'], copyright=kwargs['copyright'],
- version=kwargs['version'], credits=kwargs['credits'])['result']
+ author=kwargs['author'], email=kwargs['email'],
+ license=kwargs['license'], copyright=kwargs['copyright'],
+ version=kwargs['version'], credits=kwargs['credits'])['result']
result_lines = code_header_lines
@@ -143,8 +146,8 @@ def run(self, **kwargs):
default_block = ''
else:
default_fmt = {
- 'default': repr(cmdin.Default),
- 'default_description': repr(cmdin.DefaultDescription)
+ 'default': repr(cmdin.Default),
+ 'default_description': repr(cmdin.DefaultDescription)
}
default_block = default_block_format % default_fmt
@@ -155,23 +158,22 @@ def run(self, **kwargs):
action = 'store'
data_type = cmdin.DataType
- fmt = {'name':cmdin.Name, 'datatype':data_type, 'action':action,
- 'required':str(cmdin.Required),
- 'help':cmdin.Description, 'default_block':default_block}
+ fmt = {'name': cmdin.Name, 'datatype': data_type, 'action': action,
+ 'required': str(cmdin.Required),
+ 'help': cmdin.Description, 'default_block': default_block}
cmdin_formatted.append(input_format % fmt)
cmdout_formatted = []
for cmdin in sorted(kwargs['command'].CommandOuts.values(),
key=attrgetter('Name')):
- fmt = {'name':cmdin.Name}
+ fmt = {'name': cmdin.Name}
cmdout_formatted.append(output_format % fmt)
-
cmdin_formatted = ''.join(cmdin_formatted)
cmdout_formatted = ''.join(cmdout_formatted)
- header_fmt = {'command_module':kwargs['command_module'],
+ header_fmt = {'command_module': kwargs['command_module'],
'input_fmt': cmdin_formatted,
- 'output_fmt':cmdout_formatted}
+ 'output_fmt': cmdout_formatted}
result_lines.extend((header_format % header_fmt).split('\n'))
return {'result': result_lines}
diff --git a/pyqi/commands/make_release.py b/pyqi/commands/make_release.py
index 51103b4..0645ac7 100644
--- a/pyqi/commands/make_release.py
+++ b/pyqi/commands/make_release.py
@@ -29,21 +29,22 @@
import re
from datetime import datetime, date
from pyqi.util import pyqi_system_call
-from pyqi.core.command import (Command, CommandIn, CommandOut,
- ParameterCollection)
+from pyqi.core.command import (Command, CommandIn, CommandOut,
+ ParameterCollection)
+
class MakeRelease(Command):
BriefDescription = "Make the release"
LongDescription = "Do all the things for a release"
CommandIns = ParameterCollection([
CommandIn(Name='package_name', DataType=str,
- Description='The name of the package to release',
+ Description='The name of the package to release',
Required=True),
- CommandIn(Name='real_run', DataType=bool,
+ CommandIn(Name='real_run', DataType=bool,
Description='Perform a real run', Required=False,
Default=False)
])
-
+
CommandOuts = ParameterCollection([])
RealRun = False
_date_clean_re = re.compile(r'(\d+)(st|nd|rd|th)')
@@ -65,7 +66,7 @@ def _parse_changelog(self, pkg_name):
break
match = re.search(r'released on (\w+\s+\d+\w+\s+\d+)',
- change_info)
+ change_info)
if match is None:
continue
@@ -84,15 +85,17 @@ def _parse_date(self, string):
string = self._date_clean_re.sub(r'\1', string)
return datetime.strptime(string, '%B %d %Y')
- def _set_filename_version(self,filename,version_number,pattern):
+ def _set_filename_version(self, filename, version_number, pattern):
changed = []
+
def inject_version(match):
before, old, after = match.groups()
changed.append(True)
return before + version_number + after
with open(filename) as f:
- contents = re.sub(r"""^(\s*%s\s*=\s*(?:'|"))(.+?)((?:'|"))(?sm)""" %
- pattern, inject_version, f.read())
+ contents = re.sub(
+ r"""^(\s*%s\s*=\s*(?:'|"))(.+?)((?:'|"))(?sm)""" %
+ pattern, inject_version, f.read())
if not changed:
self._fail('Could not find %s in %s', pattern, filename)
@@ -103,7 +106,7 @@ def inject_version(match):
def _set_init_version(self, pkg_name, version):
self._info('Setting __init__.py version to %s', version)
- self._set_filename_version('%s/__init__.py' % pkg_name, version,
+ self._set_filename_version('%s/__init__.py' % pkg_name, version,
'__version__')
def _set_setup_version(self, version):
@@ -119,11 +122,11 @@ def _build_and_upload(self):
stdout, stderr, retval = pyqi_system_call(cmd, shell=False,
dry_run=not self.RealRun)
if retval is not 0:
- self._fail("build and upload failed,\nSTDOUT:\n%s\n\nSTDERR:\n%s",
+ self._fail("build and upload failed,\nSTDOUT:\n%s\n\nSTDERR:\n%s",
stdout, stderr)
def _fail(self, message, *args):
- sys.stderr.write('Error: ')
+ sys.stderr.write('Error: ')
sys.stderr.write(message % args)
sys.stderr.write('\n')
sys.exit(1)
@@ -137,13 +140,13 @@ def _get_git_tags(self):
stdout, stderr, retval = pyqi_system_call(cmd, shell=False,
dry_run=not self.RealRun)
if retval is not 0:
- self._fail("Could not git tag, \nSTDOUT:\n%s\n\nSTDERR:\n%s",
+ self._fail("Could not git tag, \nSTDOUT:\n%s\n\nSTDERR:\n%s",
stdout, stderr)
return stdout.splitlines()
def _git_is_clean(self):
- cmd = ['git','diff','--quiet']
+ cmd = ['git', 'diff', '--quiet']
# always execute, even in dry run
stdout, stderr, retval = pyqi_system_call(cmd, shell=False)
@@ -155,7 +158,7 @@ def _make_git_commit(self, message, *args):
stdout, stderr, retval = pyqi_system_call(cmd, shell=False,
dry_run=not self.RealRun)
if retval is not 0:
- self._fail("Could not git commit, \nSTDOUT:\n%s\n\nSTDERR:\n%s",
+ self._fail("Could not git commit, \nSTDOUT:\n%s\n\nSTDERR:\n%s",
stdout, stderr)
def _make_git_tag(self, tag):
@@ -164,32 +167,35 @@ def _make_git_tag(self, tag):
stdout, stderr, retval = pyqi_system_call(cmd, shell=False,
dry_run=not self.RealRun)
if retval is not 0:
- self._fail("Could not git tag, \nSTDOUT:\n%s\n\nSTDERR:\n%s",stdout,
- stderr)
+ self._fail(
+ "Could not git tag, \nSTDOUT:\n%s\n\nSTDERR:\n%s", stdout,
+ stderr)
def _get_git_branch(self):
- cmd = ['git','rev-parse','--abbrev-ref','HEAD']
+ cmd = ['git', 'rev-parse', '--abbrev-ref', 'HEAD']
# ignoring self.RealRun, always execute
stdout, stderr, retval = pyqi_system_call(cmd, shell=False)
if retval is not 0:
- self._fail("Could not get git branch, \nSTDOUT:\n%s\n\nSTDERR:\n%s",
- stdout, stderr)
+ self._fail(
+ "Could not get git branch, \nSTDOUT:\n%s\n\nSTDERR:\n%s",
+ stdout, stderr)
return stdout.strip()
def _git_push_branch(self):
branch = self._get_git_branch()
self._info('Pushing branch %s to origin', branch)
- cmd = ['git','push','upstream', branch]
+ cmd = ['git', 'push', 'upstream', branch]
stdout, stderr, retval = pyqi_system_call(cmd, shell=False,
dry_run=not self.RealRun)
if retval is not 0:
- self._fail("Could not push branch %s, \nSTDOUT:\n%s\n\nSTDERR:\n%s",
- stdout, stderr, branch)
+ self._fail(
+ "Could not push branch %s, \nSTDOUT:\n%s\n\nSTDERR:\n%s",
+ stdout, stderr, branch)
def _git_push_tag(self, tag):
self._info('Pushing tag "%s"', tag)
- cmd = ['git','push','upstream',tag]
+ cmd = ['git', 'push', 'upstream', tag]
stdout, stderr, retval = pyqi_system_call(cmd, shell=False,
dry_run=not self.RealRun)
if retval is not 0:
@@ -222,9 +228,9 @@ def run(self, **kwargs):
if version in tags:
self._fail('Version "%s" is already tagged', version)
if release_date.date() != date.today():
- self._fail('Release date is not today (%s != %s)',
- release_date.strftime('%Y-%m-%d'),
- date.today())
+ self._fail('Release date is not today (%s != %s)',
+ release_date.strftime('%Y-%m-%d'),
+ date.today())
if not self._git_is_clean():
self._fail('You have uncommitted changes in git')
diff --git a/pyqi/commands/serve_html_interface.py b/pyqi/commands/serve_html_interface.py
index 7376f92..65b1184 100644
--- a/pyqi/commands/serve_html_interface.py
+++ b/pyqi/commands/serve_html_interface.py
@@ -12,13 +12,15 @@
__credits__ = ["Evan Bolyen"]
-from pyqi.core.command import (Command, CommandIn, CommandOut, ParameterCollection)
+from pyqi.core.command import (
+ Command, CommandIn, CommandOut, ParameterCollection)
from pyqi.core.interfaces.html import start_server
+
class ServeHTMLInterface(Command):
BriefDescription = "Start the HTMLInterface server"
LongDescription = ("Start the HTMLInterface server and load the provided "
- "interface_module and port")
+ "interface_module and port")
CommandIns = ParameterCollection([
CommandIn(Name='port', DataType=int,
Description='The port to run the server on', Required=False,
@@ -30,10 +32,10 @@ class ServeHTMLInterface(Command):
])
CommandOuts = ParameterCollection([
- CommandOut(Name='result',DataType=str,
- Description='Signals the termination of the HTMLInterface '
- 'server')
- ])
+ CommandOut(Name='result', DataType=str,
+ Description='Signals the termination of the HTMLInterface '
+ 'server')
+ ])
def run(self, **kwargs):
"""Start the HTMLInterface server with the port and interface_module"""
diff --git a/pyqi/core/command.py b/pyqi/core/command.py
index b497a15..8711b4f 100644
--- a/pyqi/core/command.py
+++ b/pyqi/core/command.py
@@ -12,7 +12,6 @@
__credits__ = ["Greg Caporaso", "Daniel McDonald", "Doug Wendel",
"Jai Ram Rideout"]
-import sys, traceback
import re
from pyqi.core.log import NullLogger
from pyqi.core.exception import (IncompetentDeveloperError,
@@ -20,7 +19,9 @@
UnknownParameterError,
MissingParameterError)
+
class Parameter(object):
+
"""The ``Command`` variable type baseclass
A ``Parameter`` is interface agnostic, and is used to describe an input
@@ -29,14 +30,14 @@ class Parameter(object):
def __init__(self, Name, DataType, Description, ValidateValue=None):
"""
-
+
``Name`` should be a valid Python name so that users can supply either
a dictionary as input or named arguments.
``DataType`` specifies the type that the input must be. The input
should be an instance of type ``DataType``.
- ``ValidateValue`` can be set as a function that will validate the
+ ``ValidateValue`` can be set as a function that will validate the
value associated with a ``Parameter``.
"""
if not self._is_valid_name(Name):
@@ -46,7 +47,6 @@ def __init__(self, Name, DataType, Description, ValidateValue=None):
"start with a letter or "
"underscore." % Name)
-
self.Name = Name
self.DataType = DataType
self.Description = Description
@@ -65,29 +65,40 @@ def _pythonize(self, name):
return name
+
class CommandIn(Parameter):
+
"""A ``Command`` input variable type"""
- def __init__(self, Name, DataType, Description, Required=False,
+
+ def __init__(self, Name, DataType, Description, Required=False,
Default=None, DefaultDescription=None, **kwargs):
self.Required = Required
self.Default = Default
self.DefaultDescription = DefaultDescription
-
+
if Required and Default is not None:
raise IncompetentDeveloperError("Found required CommandIn '%s' "
- "with default value '%r'. Required CommandIns cannot have "
- "default values." % (Name, Default))
-
+ "with default value '%r'. "
+ "Required CommandIns cannot have "
+ "default values." %
+ (Name, Default))
+
super(CommandIn, self).__init__(Name, DataType, Description, **kwargs)
+
class CommandOut(Parameter):
+
"""A ``Command`` output variable type"""
+
def __init__(self, Name, DataType, Description, **kwargs):
- super(CommandOut, self).__init__(Name, DataType, Description,
+ super(CommandOut, self).__init__(Name, DataType, Description,
**kwargs)
+
class ParameterCollection(dict):
+
"""A collection of parameters with dict like lookup"""
+
def __init__(self, Parameters):
self.Parameters = Parameters
@@ -109,15 +120,17 @@ def __setitem__(self, key, val):
raise TypeError("ParameterCollections are immutable")
__delattr__ = __setitem__
+
class Command(object):
+
"""Base class for ``Command``
- A ``Command`` is interface agnostic, knows how to run itself and knows
+ A ``Command`` is interface agnostic, knows how to run itself and knows
about the arguments that it can take (via ``Parameters``).
"""
- BriefDescription = "" # 1 sentence description
- LongDescription = """""" # longer, more detailed description
+ BriefDescription = "" # 1 sentence description
+ LongDescription = """""" # longer, more detailed description
CommandIns = ParameterCollection([])
CommandOuts = ParameterCollection([])
@@ -129,7 +142,7 @@ def __call__(self, **kwargs):
"""Safely execute a ``Command``"""
self_str = str(self.__class__)
self._logger.info('Starting command: %s' % self_str)
-
+
self._validate_kwargs(kwargs)
self._set_defaults(kwargs)
@@ -155,7 +168,7 @@ def __call__(self, **kwargs):
def _validate_kwargs(self, kwargs):
"""Validate input kwargs prior to executing a ``Command``
-
+
This method can be overridden by subclasses. The baseclass defines only
a basic validation.
"""
@@ -164,7 +177,7 @@ def _validate_kwargs(self, kwargs):
# check required parameters
for p in self.CommandIns.values():
if p.Required and p.Name not in kwargs:
- err_msg = 'Missing required CommandIn %s in %s' % (p.Name,
+ err_msg = 'Missing required CommandIn %s in %s' % (p.Name,
self_str)
self._logger.fatal(err_msg)
raise MissingParameterError(err_msg)
@@ -172,7 +185,7 @@ def _validate_kwargs(self, kwargs):
if p.Name in kwargs and p.ValidateValue:
if not p.ValidateValue(kwargs[p.Name]):
err_msg = "CommandIn %s cannot take value %s in %s" % \
- (p.Name, kwargs[p.Name], self_str)
+ (p.Name, kwargs[p.Name], self_str)
self._logger.fatal(err_msg)
raise ValueError(err_msg)
@@ -182,7 +195,7 @@ def _validate_kwargs(self, kwargs):
err_msg = 'Unknown CommandIn %s in %s' % (opt, self_str)
self._logger.fatal(err_msg)
raise UnknownParameterError(err_msg)
-
+
def _validate_result(self, result):
"""Validate the result from a ``Command.run``"""
self_str = str(self.__class__)
@@ -206,13 +219,15 @@ def _set_defaults(self, kwargs):
def run(self, **kwargs):
"""Exexcute a ``Command``
-
+
A ``Command`` must accept **kwargs to run, and must return a ``dict``
as a result.
"""
raise NotImplementedError("All subclasses must implement run.")
# I do not like this
+
+
def make_command_in_collection_lookup_f(obj):
"""Return a function for convenient ``CommandIns`` lookup.
@@ -222,6 +237,7 @@ def lookup_f(name):
return obj.CommandIns[name]
return lookup_f
+
def make_command_out_collection_lookup_f(obj):
"""Return a function for convenient ``CommandOuts`` lookup.
diff --git a/pyqi/core/container.py b/pyqi/core/container.py
index d871dbc..73d336f 100644
--- a/pyqi/core/container.py
+++ b/pyqi/core/container.py
@@ -11,66 +11,73 @@
__credits__ = ["Greg Caporaso", "Daniel McDonald", "Doug Wendel",
"Jai Ram Rideout"]
+
class ContainerError(Exception):
pass
-
+
+
class CannotReadError(ContainerError):
pass
-
+
+
class CannotWriteError(ContainerError):
pass
-
+
+
class Passthrough(object):
+
"""Basic pass through container class"""
- _reserved = set(['_reserved','TypeName', 'Info'])
+ _reserved = set(['_reserved', 'TypeName', 'Info'])
TypeName = "Passthrough"
Info = None
-
+
def __init__(self, *args, **kwargs):
if 'Info' in kwargs:
super(Passthrough, self).__setattr__('Info', kwargs['Info'])
-
+
def _load_if_needed(self):
raise NotImplementedError("Passthrough cannot issue I/O.")
-
+
def __getattr__(self, attr):
"""Pass through to contained class if the attribute is not recognized"""
if attr in super(Passthrough, self).__getattribute__('_reserved'):
return super(Passthrough, self).__getattribute__(attr)
-
+
self._load_if_needed()
-
+
return getattr(self._object, attr)
-
+
def __setattr__(self, attr, val):
"""Pass through to contained class if the attribute is not recognized"""
if attr in super(Passthrough, self).__getattribute__('_reserved'):
return super(Passthrough, self).__setattr__(attr, val)
-
+
self._load_if_needed()
-
+
setattr(self._object, attr, val)
-
+
def __hasattr__(self, attr):
"""Pass through to contained class if the attribute is not recognized"""
if attr in super(Passthrough, self).__getattribute__('_reserved'):
return True
-
+
self._load_if_needed()
-
+
return hasattr(self._object, attr)
+
class PassthroughIO(Passthrough):
_reserved = set(['_reserved', 'TypeName', '_reader', '_writer',
- '_object', 'InPath', 'OutPath','read', 'write',
+ '_object', 'InPath', 'OutPath', 'read', 'write',
'_load_if_needed', 'Info'])
TypeName = "PassthroughIO"
-
+
def __init__(self, *args, **kwargs):
super(PassthroughIO, self).__init__(*args, **kwargs)
-
+
if 'Object' in kwargs:
- super(PassthroughIO, self).__setattr__('_object', kwargs['Object'])
+ super(PassthroughIO, self).__setattr__(
+ '_object', kwargs['Object'])
else:
self._object = None
@@ -80,7 +87,8 @@ def __init__(self, *args, **kwargs):
self.InPath = None
if 'OutPath' in kwargs:
- super(PassthroughIO, self).__setattr__('OutPath', kwargs['OutPath'])
+ super(PassthroughIO, self).__setattr__(
+ 'OutPath', kwargs['OutPath'])
else:
self.OutPath = None
@@ -91,7 +99,7 @@ def _load_if_needed(self):
self._object = self._reader(self, self.InPath)
else:
raise CannotReadError("No object and InPath is None.")
-
+
def read(self):
"""Attempt to read"""
if self._object is None:
@@ -109,80 +117,98 @@ def write(self):
self._writer(self, self.OutPath)
self._object = None
+
class PassthroughRead(PassthroughIO):
+
def __init__(self, *args, **kwargs):
if 'reader' in kwargs:
- super(PassthroughRead, self).__setattr__('_reader', kwargs['reader'])
+ super(PassthroughRead, self).__setattr__(
+ '_reader', kwargs['reader'])
else:
raise ContainerError("A reader is required.")
super(PassthroughRead, self).__init__(*args, **kwargs)
+
class PassthroughWrite(PassthroughIO):
+
def __init__(self, *args, **kwargs):
if 'writer' in kwargs:
- super(PassthroughWrite, self).__setattr__('_reader', kwargs['writer'])
+ super(PassthroughWrite, self).__setattr__(
+ '_reader', kwargs['writer'])
else:
raise ContainerError("A writer is required.")
super(PassthroughWrite, self).__init__(*args, **kwargs)
+
class DelayRead(PassthroughRead):
+
"""Contain an object and issue IO when an object attribute is requested"""
TypeName = "DelayRead"
-
+
+
class DelayWrite(PassthroughWrite):
+
"""Contain an object and issue IO with the container is no more"""
TypeName = "DelayWrite"
def __del__(self):
self.write()
+
class ImmediateRead(PassthroughRead):
+
"""Issue an immediate read on construction"""
TypeName = "ImmediateRead"
-
+
def __init__(self, *args, **kwargs):
super(ImmediateRead, self).__init__(*args, **kwargs)
- self.read()
+ self.read()
+
class ImmediateWrite(PassthroughWrite):
TypeName = "ImmediateWrite"
-
+
def __init__(self, *args, **kwargs):
super(ImmediateWrite, self).__init__(*args, **kwargs)
- self.write()
+ self.write()
+
def default_write_str(obj, path):
- f = open(path,'w')
+ f = open(path, 'w')
f.write(str(obj._object))
f.close()
+
def default_read_str(obj, path):
f = open(path)
return f.read()
+
def default_write_object(obj, path):
f = open(path, 'w')
f.write(repr(obj._object))
f.close()
+
def default_read_object(obj, path):
f = open(path)
- return f.read() # eval isn't safe...
+ return f.read() # eval isn't safe...
+
+IOType = {'ImmediateRead': ImmediateRead,
+ 'ImmediateWrite': ImmediateWrite,
+ 'DelayRead': DelayRead,
+ 'DelayWrite': DelayWrite}
-IOType = {'ImmediateRead':ImmediateRead,
- 'ImmediateWrite':ImmediateWrite,
- 'DelayRead':DelayRead,
- 'DelayWrite':DelayWrite}
+IOLookup = {str: (default_read_str, default_write_str)}
-IOLookup = {str:(default_read_str, default_write_str)}
def WithIO(obj, IO_type=None, IO_lookup=None, **kwargs):
if IO_type is None:
raise ContainerError("IO_type is required.")
-
+
if IO_type not in IOType:
raise ContainerError("Unknown IO_type: %s" % IO_type)
-
+
if kwargs is None:
kwargs = {}
@@ -196,11 +222,12 @@ def WithIO(obj, IO_type=None, IO_lookup=None, **kwargs):
reader, writer = IO_lookup[obj_type]
else:
reader, writer = default_read_object, default_write_object
-
+
kwargs['reader'] = reader
kwargs['writer'] = writer
return IOType[IO_type](**kwargs)
+
def WithoutIO(obj, **kwargs):
return obj
diff --git a/pyqi/core/exception.py b/pyqi/core/exception.py
index 0a55a42..3772cbe 100644
--- a/pyqi/core/exception.py
+++ b/pyqi/core/exception.py
@@ -11,20 +11,26 @@
__credits__ = ["Greg Caporaso", "Daniel McDonald", "Doug Wendel",
"Jai Ram Rideout"]
+
class CommandError(Exception):
pass
+
class IncompetentDeveloperError(CommandError):
pass
+
class MissingParameterError(CommandError):
pass
+
class InvalidReturnTypeError(IncompetentDeveloperError):
pass
+
class UnknownParameterError(IncompetentDeveloperError):
pass
+
class MissingVersionInfoError(IncompetentDeveloperError):
pass
diff --git a/pyqi/core/factory.py b/pyqi/core/factory.py
index f13278f..e255e7a 100644
--- a/pyqi/core/factory.py
+++ b/pyqi/core/factory.py
@@ -11,18 +11,24 @@
__credits__ = ["Greg Caporaso", "Daniel McDonald", "Doug Wendel",
"Jai Ram Rideout"]
+
def general_factory(command_constructor, usage_examples, inputs, outputs,
version, interface=None):
"""Generalized interface factory"""
class IObject(interface):
+
"""Dynamic interface object"""
CommandConstructor = command_constructor
+
def _get_usage_examples(self):
return usage_examples
+
def _get_inputs(self):
return inputs
+
def _get_outputs(self):
return outputs
+
def _get_version(self):
return version
diff --git a/pyqi/core/interface.py b/pyqi/core/interface.py
index c2e1cd1..ff1f8f5 100644
--- a/pyqi/core/interface.py
+++ b/pyqi/core/interface.py
@@ -13,11 +13,12 @@
import importlib
from sys import exit, stderr
-from ConfigParser import SafeConfigParser
from glob import glob
-from os.path import basename, dirname, expanduser, join
+from os.path import basename, dirname, join
+
from pyqi.core.exception import IncompetentDeveloperError
+
class Interface(object):
CommandConstructor = None
@@ -33,7 +34,7 @@ def __init__(self, **kwargs):
self._validate_usage_examples(self._get_usage_examples())
self._validate_inputs_outputs(self._get_inputs(), self._get_outputs())
-
+
def __call__(self, in_, *args, **kwargs):
self._the_in_validator(in_)
cmd_input = self._input_handler(in_, *args, **kwargs)
@@ -79,10 +80,10 @@ def _validate_inputs_outputs(self, inputs, outputs):
for ifout in outputs:
if ifout.InputName is None:
continue
-
+
if ifout.InputName not in input_names:
- raise IncompetentDeveloperError(\
- "Could not link %s to an input!" % ifout.InputName)
+ raise IncompetentDeveloperError(
+ "Could not link %s to an input!" % ifout.InputName)
def _the_in_validator(self, in_):
"""The job securator"""
@@ -104,7 +105,7 @@ def _output_handler(self, results):
def _get_usage_examples(self):
"""Return a list of ``InterfaceUsageExample`` objects
-
+
These are typically set in a command+interface specific configuration
file and passed to ``pyqi.core.general_factory``
"""
@@ -112,7 +113,7 @@ def _get_usage_examples(self):
def _get_inputs(self):
"""Return a list of ``InterfaceOption`` objects
-
+
These are typically set in a command+interface specific configuration
file and passed to ``pyqi.core.general_factory``
"""
@@ -120,7 +121,7 @@ def _get_inputs(self):
def _get_outputs(self):
"""Return a list of ``InterfaceOutputOption`` objects
-
+
These are typically set in a command+interface specific configuration
file and passed to ``pyqi.core.general_factory``
"""
@@ -128,33 +129,37 @@ def _get_outputs(self):
def _get_version(self):
"""Return a version string, e.g., ``'0.1'``
-
+
This is typically set in a command+interface specific configuration
file and passed to ``pyqi.core.general_factory``
"""
raise NotImplementedError("Must define _get_version")
+
class InterfaceOption(object):
+
"""Describes an option and what to do with it
-
- ``Parameter`` is a pyqi.core.command.Parameter instance, typically an
+
+ ``Parameter`` is a pyqi.core.command.Parameter instance, typically an
instance of a ``CommandIn`` or a ``CommandOut``.
``Type`` refers to the interface type, not the actually datatype of the
``Parameter``. For instance, a file path may be specified on a command
- line interface for a BIOM table. The ``Parameter.Datatype`` is a BIOM
- type, while the ``InterfaceOption.Type`` is a string or possibly a
+ line interface for a BIOM table. The ``Parameter.Datatype`` is a BIOM
+ type, while the ``InterfaceOption.Type`` is a string or possibly a
``FilePath`` object.
- ``Handler`` is a function that either transforms the value associated with
+ ``Handler`` is a function that either transforms the value associated with
the option into the ``Parameter.DataType`` (e.g., if ``Parameter`` is a
- ``CommandIn``), or the result of a ``Command`` into something consumable
- by the interface (e.g., if ``Parameter`` is a ``CommandOut``).
+ ``CommandIn``), or the result of a ``Command`` into something
+ consumable by the interface (e.g., if ``Parameter`` is a
+ ``CommandOut``).
``Name`` is the name of the ``InterfaceOption``, e.g,, 'input-fp'
``Help`` is a description of the ``InterfaceOption``
"""
+
def __init__(self, Parameter=None, Type=None, Handler=None, Name=None,
Help=None):
self.Parameter = Parameter
-
+
if self.Parameter is None:
if Name is None:
raise IncompetentDeveloperError("Must specify a Name for the "
@@ -171,8 +176,8 @@ def __init__(self, Parameter=None, Type=None, Handler=None, Name=None,
# Transfer information from Parameter unless overridden here.
self.Name = Parameter.Name if Name is None else Name
self.Help = Parameter.Description if Help is None else Help
-
- # This information is never contained in a Parameter.
+
+ # This information is never contained in a Parameter.
self.Type = Type
self.Handler = Handler
@@ -186,6 +191,7 @@ def getParameterName(self):
else:
return self.Parameter.Name
+
class InterfaceInputOption(InterfaceOption):
_primitive_mapping = {
"None": None,
@@ -203,8 +209,8 @@ class InterfaceInputOption(InterfaceOption):
"frozenset": frozenset
}
- def __init__(self, Action=None, Required=False, Default=None,
- ShortName=None, DefaultDescription=None,
+ def __init__(self, Action=None, Required=False, Default=None,
+ ShortName=None, DefaultDescription=None,
convert_to_dashed_name=True, **kwargs):
super(InterfaceInputOption, self).__init__(**kwargs)
@@ -213,44 +219,52 @@ def __init__(self, Action=None, Required=False, Default=None,
self.DefaultDescription = DefaultDescription
self.ShortName = ShortName
self.Action = Action
-
+
if convert_to_dashed_name:
self.Name = self.Name.replace('_', '-')
if self.Required and self.Default is not None:
raise IncompetentDeveloperError("Found required option '%s' "
- "with default value '%s'. Required options cannot have "
- "default values." % (self.Name, self.Default))
-
+ "with default value '%s'. "
+ "Required options cannot have "
+ "default values." %
+ (self.Name, self.Default))
+
if self.Default is None and self.Parameter is not None:
self.Default = self.Parameter.Default
- self.DefaultDescription = self.Parameter.DefaultDescription
+ self.DefaultDescription = self.Parameter.DefaultDescription
# If a parameter is required, the option is always required, but
# if a parameter is not required, but the option does require it,
# then we make the option required.
- if self.Parameter is not None:
+ if self.Parameter is not None:
self.Required = self.Parameter.Required
if not self.Parameter.Required and Required:
self.Required = True
-
+
self._convert_primitive_strings()
self._validate_option()
def _convert_primitive_strings(self):
- """Convert our Type to a python type object if it is a primitive string.
+ """Convert our Type to a python type if possible
+
Otherwise, leave unchanged"""
self.Type = self._primitive_mapping.get(self.Type, self.Type)
+
class InterfaceOutputOption(InterfaceOption):
+
def __init__(self, InputName=None, **kwargs):
super(InterfaceOutputOption, self).__init__(**kwargs)
self.InputName = InputName
-class InterfaceUsageExample(object):
+
+class InterfaceUsageExample(object):
+
"""Provide structure to a usage example"""
+
def __init__(self, ShortDesc, LongDesc, Ex):
self.ShortDesc = ShortDesc
self.LongDesc = LongDesc
@@ -262,6 +276,7 @@ def _validate_usage_example(self):
"""Interface specific usage example validation"""
raise NotImplementedError("Must define in the subclass")
+
def get_command_names(config_base_name):
"""Return a list of available command names.
@@ -278,7 +293,8 @@ def get_command_names(config_base_name):
config_base_dir = dirname(config_base_module.__file__)
- # from http://stackoverflow.com/questions/1057431/loading-all-modules-in-a-folder-in-python
+ # from
+ # http://stackoverflow.com/questions/1057431/loading-all-modules-in-a-folder-in-python
command_names = CommandList()
for f in glob(join(config_base_dir, '*.py')):
command_name = basename(f)
@@ -289,6 +305,7 @@ def get_command_names(config_base_name):
return command_names
+
def get_command_config(command_config_module, cmd, exit_on_failure=True):
"""Get the configuration for a ``Command``"""
cmd_cfg = None
@@ -310,7 +327,9 @@ def get_command_config(command_config_module, cmd, exit_on_failure=True):
return cmd_cfg, error_msg
+
class CommandList(list):
+
def __init__(self):
super(CommandList, self).__init__()
diff --git a/pyqi/core/interfaces/html/__init__.py b/pyqi/core/interfaces/html/__init__.py
index eff4675..a6f3449 100644
--- a/pyqi/core/interfaces/html/__init__.py
+++ b/pyqi/core/interfaces/html/__init__.py
@@ -9,7 +9,7 @@
#-----------------------------------------------------------------------------
__credits__ = ["Evan Bolyen", "Jai Ram Rideout", "Daniel McDonald",
- "Greg Caporaso"]
+ "Greg Caporaso"]
import os
import types
@@ -19,36 +19,50 @@
from copy import copy
from glob import glob
from os.path import abspath, exists, isdir, isfile, split
-from pyqi.core.interface import (Interface, InterfaceOutputOption, InterfaceInputOption,
- InterfaceUsageExample, get_command_names, get_command_config)
+from pyqi.core.interface import (
+ Interface, InterfaceOutputOption, InterfaceInputOption,
+ InterfaceUsageExample, get_command_names, get_command_config)
from pyqi.core.factory import general_factory
from pyqi.core.exception import IncompetentDeveloperError
from pyqi.core.command import Parameter
from pyqi.util import get_version_string
+
class HTMLResult(InterfaceOutputOption):
+
"""Base class for results for an HTML config file"""
+
def __init__(self, MIMEType=None, **kwargs):
super(HTMLResult, self).__init__(**kwargs)
if MIMEType is None:
- raise IncompetentDeveloperError("A valid MIMEType must be provided")
- self.MIMEType = MIMEType;
+ raise IncompetentDeveloperError(
+ "A valid MIMEType must be provided")
+ self.MIMEType = MIMEType
+
class HTMLDownload(HTMLResult):
+
"""Result class for downloading a file from the server"""
- def __init__(self, FileExtension=None, FilenameLookup=None, DefaultFilename=None,
+
+ def __init__(
+ self, FileExtension=None, FilenameLookup=None, DefaultFilename=None,
MIMEType='application/octet-stream', **kwargs):
super(HTMLDownload, self).__init__(MIMEType=MIMEType, **kwargs)
self.FileExtension = FileExtension
self.FilenameLookup = FilenameLookup
self.DefaultFilename = DefaultFilename
+
class HTMLPage(HTMLResult):
+
"""Result class for displaying a page for an HTML config file"""
+
def __init__(self, MIMEType='text/html', **kwargs):
super(HTMLPage, self).__init__(MIMEType=MIMEType, **kwargs)
+
class HTMLInputOption(InterfaceInputOption):
+
"""Define an input option for an HTML config file"""
_type_handlers = {
None: lambda: None,
@@ -76,18 +90,21 @@ def get_html(self, prefix, value=""):
"""Return the HTML needed for user input given a default value"""
if (not value) and (self.Default is not None):
value = self.Default
-
- input_name = prefix + self.Name
- string_input = lambda: '' % (input_name, value)
- number_input = lambda: '' % (input_name, value)
- #html input files cannot have default values.
- #If the html interface worked as a data service, this would be possible as submit would be ajax.
+ input_name = prefix + self.Name
+ string_input = lambda: '' % (input_name,
+ value)
+ number_input = lambda: '' % (
+ input_name, value)
+
+ # html input files cannot have default values.
+ # If the html interface worked as a data service, this would be
+ # possible as submit would be ajax.
upload_input = lambda: '' % input_name
mchoice_input = lambda: ''.join(
- [ ('(%s)'
- % (choice, input_name, choice, 'checked="true"' if value == choice else ''))
- for choice in self.Choices ]
+ [('(%s)'
+ % (choice, input_name, choice, 'checked="true"' if value == choice else ''))
+ for choice in self.Choices]
)
input_switch = {
@@ -103,19 +120,21 @@ def get_html(self, prefix, value=""):
}
return ''.join(['
',
- ('*' + self.Name) if self.Required else self.Name,
+ ('*' +
+ self.Name) if self.Required else self.Name,
'
',
- input_switch[self.Type](),
- '
',
+ input_switch[self.Type](),
+ '
',
self.Help,
- '
'
- ])
-
+ '
'
+ ])
+
def _validate_option(self):
if self.Type not in self._type_handlers:
- raise IncompetentDeveloperError("Unsupported Type in HTMLInputOption: %s" % self.Type)
+ raise IncompetentDeveloperError(
+ "Unsupported Type in HTMLInputOption: %s" % self.Type)
- #From optparse's __init__.py, inside class PyqiOption
+ # From optparse's __init__.py, inside class PyqiOption
if self.Type == "multiple_choice":
if self.Choices is None:
raise IncompetentDeveloperError(
@@ -125,13 +144,16 @@ def _validate_option(self):
"choices must be a list of strings ('%s' supplied)"
% str(type(self.Choices)).split("'")[1], self)
elif self.Choices is not None:
- raise IncompetentDeveloperError("must not supply Choices for type %r" % self.type, self)
+ raise IncompetentDeveloperError(
+ "must not supply Choices for type %r" % self.type, self)
+
class HTMLInterface(Interface):
+
"""An HTML interface"""
- #Relative mapping wasn't working on a collegue's MacBook when pyqi was run outside of it's directory
- #Until I understand why that was the case and how to fix it, I am putting the style css here.
- #This is not a permanent solution.
+ # Relative mapping wasn't working on a collegue's MacBook when pyqi was run outside of it's directory
+ # Until I understand why that was the case and how to fix it, I am putting the style css here.
+ # This is not a permanent solution.
css_style = '\n'.join([
'html, body {',
' margin: 0px;',
@@ -183,35 +205,36 @@ class HTMLInterface(Interface):
'a, a:visited, a:active{',
' color: rgb(32, 67, 92);',
'}'
- ])
+ ])
def __init__(self, input_prefix="pyqi_", **kwargs):
self._html_input_prefix = input_prefix
self._html_interface_input = {}
super(HTMLInterface, self).__init__(**kwargs)
-
- #Override
+
+ # Override
def __call__(self, in_, *args, **kwargs):
self._the_in_validator(in_)
cmd_input, errors = self._input_handler(in_, *args, **kwargs)
if errors:
return {
- 'type': 'error',
- 'errors': errors
- }
- else:
+ 'type': 'error',
+ 'errors': errors
+ }
+ else:
cmd_result = self.CmdInstance(**cmd_input)
- self._the_out_validator(cmd_result)
+ self._the_out_validator(cmd_result)
return self._output_handler(cmd_result)
def _validate_inputs_outputs(self, inputs, outputs):
- super(HTMLInterface, self)._validate_inputs_outputs(inputs, outputs)
-
+ super(HTMLInterface, self)._validate_inputs_outputs(inputs, outputs)
+
if len(outputs) > 1:
raise IncompetentDeveloperError("There can be only one... output")
- if not ( isinstance(outputs[0], HTMLPage) or isinstance(outputs[0], HTMLDownload) ):
- raise IncompetentDeveloperError("Output must subclass HTMLPage or HTMLDownload")
+ if not (isinstance(outputs[0], HTMLPage) or isinstance(outputs[0], HTMLDownload)):
+ raise IncompetentDeveloperError(
+ "Output must subclass HTMLPage or HTMLDownload")
def _validate_usage_examples(self, usage_examples):
super(HTMLInterface, self)._validate_usage_examples(usage_examples)
@@ -241,7 +264,7 @@ def _input_handler(self, in_, *args, **kwargs):
formatted_input = {}
for key in in_:
- mod_key = key[ len(self._html_input_prefix): ]
+ mod_key = key[len(self._html_input_prefix):]
formatted_input[mod_key] = in_[key]
if not formatted_input[mod_key].value:
formatted_input[mod_key] = None
@@ -255,10 +278,12 @@ def _input_handler(self, in_, *args, **kwargs):
errors.append("Error: %s is required." % option.Name)
continue
try:
- formatted_input[option.Name] = option.cast_value(formatted_input[option.Name])
+ formatted_input[option.Name] = option.cast_value(
+ formatted_input[option.Name])
except (ValueError, TypeError):
- errors.append("Error: %s must be type %s" % (option.Name, option.Type) );
+ errors.append("Error: %s must be type %s" %
+ (option.Name, option.Type))
if option.Parameter is not None:
param_name = option.getParameterName()
@@ -279,7 +304,7 @@ def _build_usage_lines(self, required_options):
def _output_download_handler(self, output, handled_results):
"""Handle the output for type: 'download' """
- #Set up the filename for download
+ # Set up the filename for download
filename = "unnamed_pyqi_output"
extension = ""
if output.FileExtension is not None:
@@ -296,17 +321,17 @@ def _output_download_handler(self, output, handled_results):
filehandle = filename + extension
return {
- 'type':'download',
- 'filename':filehandle,
- 'contents':handled_results
- }
+ 'type': 'download',
+ 'filename': filehandle,
+ 'contents': handled_results
+ }
def _output_page_handler(self, output, handled_results):
"""Handle the output for type: 'page' """
return {
- 'type':'page',
- 'mime_type':output.MIMEType,
- 'contents':handled_results
+ 'type': 'page',
+ 'mime_type': output.MIMEType,
+ 'contents': handled_results
}
def _output_handler(self, results):
@@ -319,11 +344,11 @@ def _output_handler(self, results):
if output.InputName is None:
handled_results = output.Handler(rk, results[rk])
else:
- handled_results = output.Handler(rk, results[rk],
- self._html_interface_input[output.InputName])
+ handled_results = output.Handler(rk, results[rk],
+ self._html_interface_input[output.InputName])
else:
handled_results = results[rk]
-
+
if isinstance(output, HTMLDownload):
return self._output_download_handler(output, handled_results)
@@ -332,7 +357,8 @@ def _output_handler(self, results):
def command_page_writer(self, write, errors, postvars):
"""Write an HTML page which contains a form for user input"""
- write('%s' % self.CommandName)
+ write('%s' %
+ self.CommandName)
write('')
write('
%s
' % self.CommandName)
- write(self._build_usage_lines([opt for opt in self._get_inputs() if opt.Required]))
+ write(self._build_usage_lines(
+ [opt for opt in self._get_inputs() if opt.Required]))
- write('
")
write("")
@@ -413,9 +448,9 @@ def route(self, path, output_writer):
self.send_header('Content-type', 'text/html')
self.end_headers()
output_writer(self.wfile.write)
-
+
self.wfile.close()
- self._unrouted = False;
+ self._unrouted = False
def command_route(self, command):
"""Define a route for a command and write the command page"""
@@ -426,7 +461,7 @@ def command_route(self, command):
self.send_header('Content-type', 'text/html')
self.end_headers()
cmd_obj.command_page_writer(self.wfile.write, [], {})
-
+
self.wfile.close()
self._unrouted = False
@@ -438,17 +473,18 @@ def post_route(self, command, postvars):
result = cmd_obj(postvars)
except Exception as e:
result = {
- 'type':'error',
- 'errors':[e]
+ 'type': 'error',
+ 'errors': [e]
}
-
+
if result['type'] == 'error':
self.send_response(400)
self.send_header('Content-type', 'text/html')
self.end_headers()
- cmd_obj.command_page_writer(self.wfile.write, result['errors'], postvars)
+ cmd_obj.command_page_writer(
+ self.wfile.write, result['errors'], postvars)
- elif result['type'] == 'page':
+ elif result['type'] == 'page':
self.send_response(200)
self.send_header('Content-type', result['mime_type'])
self.end_headers()
@@ -456,11 +492,13 @@ def post_route(self, command, postvars):
elif result['type'] == 'download':
self.send_response(200)
- self.send_header('Content-type', 'application/octet-stream')
- self.send_header('Content-disposition', 'attachment; filename='+result['filename'])
+ self.send_header(
+ 'Content-type', 'application/octet-stream')
+ self.send_header('Content-disposition',
+ 'attachment; filename=' + result['filename'])
self.end_headers()
self.wfile.write(result['contents'])
-
+
self.wfile.close()
self._unrouted = False
@@ -480,8 +518,9 @@ def do_GET(self):
self.route("/index", self.index)
self.route("/home", self.index)
- def r(write):#host.domain.tld/help
- write("This is still a very in development interface, there is no help.")
+ def r(write): # host.domain.tld/help
+ write(
+ "This is still a very in development interface, there is no help.")
self.route("/help", r)
for command in module_commands:
@@ -492,19 +531,20 @@ def r(write):#host.domain.tld/help
def do_POST(self):
"""Handle POST requests"""
postvars = FieldStorage(fp=self.rfile,
- headers=self.headers,
- environ={'REQUEST_METHOD':'POST',
- 'CONTENT_TYPE':self.headers['Content-Type']})
+ headers=self.headers,
+ environ={'REQUEST_METHOD': 'POST',
+ 'CONTENT_TYPE': self.headers['Content-Type']})
for command in module_commands:
self.post_route(command, postvars)
self.end_routes()
-
return HTMLInterfaceHTTPHandler
-#This will generally be called from a generated command.
+# This will generally be called from a generated command.
+
+
def start_server(port, module):
"""Start a server for the HTMLInterface on the specified port"""
interface_server = HTTPServer(("", port), get_http_handler(module))
diff --git a/pyqi/core/interfaces/html/input_handler.py b/pyqi/core/interfaces/html/input_handler.py
index 7cedd9f..443a09d 100644
--- a/pyqi/core/interfaces/html/input_handler.py
+++ b/pyqi/core/interfaces/html/input_handler.py
@@ -13,6 +13,7 @@
from pyqi.core.exception import IncompetentDeveloperError
+
def load_file_lines(option_value):
"""Return a list of strings, one per line in the file.
@@ -20,12 +21,13 @@ def load_file_lines(option_value):
"""
if not hasattr(option_value, 'read'):
raise IncompetentDeveloperError("Input type must be a file object.")
-
+
return [line.strip() for line in option_value]
+
def load_file_contents(option_value):
"""Return the contents of a file as a single string."""
if not hasattr(option_value, 'read'):
raise IncompetentDeveloperError("Input type must be a file object.")
-
+
return option_value.read()
diff --git a/pyqi/core/interfaces/html/output_handler.py b/pyqi/core/interfaces/html/output_handler.py
index b24ad00..2868aeb 100644
--- a/pyqi/core/interfaces/html/output_handler.py
+++ b/pyqi/core/interfaces/html/output_handler.py
@@ -11,10 +11,12 @@
__credits__ = ["Evan Bolyen"]
+
def newline_list_of_strings(result_key, data, option_value=None):
"""Return a string from a list of strings while appending newline"""
return "\n".join(data)
+
def html_list_of_strings(result_key, data, option_value=None):
"""Return a string from a list of strings while appending an html break"""
return " ".join(data)
diff --git a/pyqi/core/interfaces/optparse/__init__.py b/pyqi/core/interfaces/optparse/__init__.py
index dd43c8c..7db2f3d 100644
--- a/pyqi/core/interfaces/optparse/__init__.py
+++ b/pyqi/core/interfaces/optparse/__init__.py
@@ -12,27 +12,29 @@
"Rob Knight", "Doug Wendel", "Jai Ram Rideout",
"Jose Antonio Navas Molina"]
-import os
import types
from copy import copy
from glob import glob
from os.path import abspath, exists, isdir, isfile, split
from optparse import (Option, OptionParser, OptionGroup, OptionValueError,
OptionError)
-from pyqi.core.interface import (Interface, InterfaceInputOption,
+from pyqi.core.interface import (Interface, InterfaceInputOption,
InterfaceOutputOption, InterfaceUsageExample)
from pyqi.core.factory import general_factory
from pyqi.core.exception import IncompetentDeveloperError
-from pyqi.core.command import Parameter
+
class OptparseResult(InterfaceOutputOption):
+
def __init__(self, **kwargs):
super(OptparseResult, self).__init__(**kwargs)
def _validate_option(self):
pass
+
class OptparseOption(InterfaceInputOption):
+
"""An augmented option that expands a ``CommandIn`` into an Option"""
def __init__(self, **kwargs):
@@ -82,8 +84,11 @@ def getOptparseOption(self):
default=self.Default)
return option
+
class OptparseUsageExample(InterfaceUsageExample):
+
"""Provide structure to a usage example"""
+
def _validate_usage_example(self):
if self.ShortDesc is None:
raise IncompetentDeveloperError("Must define ShortDesc")
@@ -92,13 +97,15 @@ def _validate_usage_example(self):
if self.Ex is None:
raise IncompetentDeveloperError("Must define Ex")
+
class OptparseInterface(Interface):
+
"""A command line interface"""
DisallowPositionalArguments = True
- HelpOnNoArguments = True
+ HelpOnNoArguments = True
OptionalInputLine = '[] indicates optional input (order unimportant)'
RequiredInputLine = '{} indicates required input (order unimportant)'
-
+
def __init__(self, **kwargs):
super(OptparseInterface, self).__init__(**kwargs)
@@ -164,8 +171,9 @@ def _input_handler(self, in_, *args, **kwargs):
# an error.
if self.DisallowPositionalArguments and len(args) != 0:
parser.error("Positional argument detected: %s\n" % str(args[0]) +
- " Be sure all parameters are identified by their option name.\n" +
- " (e.g.: include the '-i' in '-i INPUT_DIR')")
+ " Be sure all parameters are identified by their"
+ " option name.\n(e.g.: include the '-i' in "
+ "'-i INPUT_DIR')")
# Test that all required options were provided.
if required_opts:
@@ -186,13 +194,13 @@ def _input_handler(self, in_, *args, **kwargs):
if option.Parameter is not None:
param_name = option.getParameterName()
optparse_clean_name = \
- self._get_optparse_clean_name(option.Name)
+ self._get_optparse_clean_name(option.Name)
if option.Handler is None:
value = self._optparse_input[optparse_clean_name]
else:
value = option.Handler(
- self._optparse_input[optparse_clean_name])
+ self._optparse_input[optparse_clean_name])
cmd_input_kwargs[param_name] = value
@@ -201,7 +209,7 @@ def _input_handler(self, in_, *args, **kwargs):
def _build_usage_lines(self, required_options):
""" Build the usage string from components """
line1 = 'usage: %prog [options] ' + \
- '{%s}' % ' '.join(['%s %s' % (str(rp),rp.Name.upper())
+ '{%s}' % ' '.join(['%s %s' % (str(rp), rp.Name.upper())
for rp in required_options])
formatted_usage_examples = []
@@ -211,22 +219,22 @@ def _build_usage_lines(self, required_options):
example = usage_example.Ex.strip()
if short_description:
- formatted_usage_examples.append('%s: %s\n %s' %
+ formatted_usage_examples.append('%s: %s\n %s' %
(short_description,
long_description, example))
else:
formatted_usage_examples.append('%s\n %s' %
- (long_description,example))
+ (long_description, example))
formatted_usage_examples = '\n\n'.join(formatted_usage_examples)
lines = (line1,
- '', # Blank line
+ '', # Blank line
self.OptionalInputLine,
self.RequiredInputLine,
- '', # Blank line
+ '', # Blank line
self.CmdInstance.LongDescription,
- '', # Blank line
+ '', # Blank line
'Example usage: ',
'Print help message and exit',
' %prog -h\n',
@@ -240,15 +248,15 @@ def _output_handler(self, results):
for output in self._get_outputs():
rk = output.Name
-
+
if output.InputName is None:
handled_results[rk] = output.Handler(rk, results[rk])
else:
optparse_clean_name = \
- self._get_optparse_clean_name(output.InputName)
+ self._get_optparse_clean_name(output.InputName)
opt_value = self._optparse_input[optparse_clean_name]
handled_results[rk] = output.Handler(rk, results[rk],
- opt_value)
+ opt_value)
return handled_results
@@ -256,24 +264,26 @@ def _get_optparse_clean_name(self, name):
# optparse converts dashes to underscores in long option names.
return name.replace('-', '_')
+
def optparse_factory(command_constructor, usage_examples, inputs, outputs,
version):
"""Optparse command line interface factory
-
+
command_constructor - a subclass of ``Command``
usage_examples - usage examples for using ``command_constructor`` via a
command line interface.
inputs - config ``inputs`` or a list of ``OptparseOptions``
- outputs - config ``outputs`` or a list of ``OptparseResults``
+ outputs - config ``outputs`` or a list of ``OptparseResults``
version - config ``__version__`` (a version string)
"""
return general_factory(command_constructor, usage_examples, inputs,
outputs, version, OptparseInterface)
+
def optparse_main(interface_object, local_argv):
"""Construct and execute an interface object"""
optparse_cmd = interface_object()
- result = optparse_cmd(local_argv[1:])
+ _ = optparse_cmd(local_argv[1:])
return 0
# Definition of PyqiOption option type, a subclass of Option that contains
@@ -289,32 +299,36 @@ def optparse_main(interface_object, local_argv):
# TODO: this code needs to be refactored to better fit the pyqi framework.
# Should probably get added to the OptparseInterface class.
+
def check_existing_filepath(option, opt, value):
if not exists(value):
raise OptionValueError(
"option %s: file does not exist: %r" % (opt, value))
elif not isfile(value):
raise OptionValueError(
- "option %s: not a regular file (can't be a directory!): %r" % (opt, value))
+ "option %s: not a regular file (can't be a directory!): %r" %
+ (opt, value))
else:
return value
+
def check_existing_filepaths(option, opt, value):
paths = []
for v in value.split(','):
fps = glob(v)
if len(fps) == 0:
raise OptionValueError(
- "No filepaths match pattern/name '%s'. "
- "All patterns must be matched at least once." % v)
+ "No filepaths match pattern/name '%s'. "
+ "All patterns must be matched at least once." % v)
else:
paths.extend(fps)
values = []
for v in paths:
- check_existing_filepath(option,opt,v)
+ check_existing_filepath(option, opt, v)
values.append(v)
return values
+
def check_existing_dirpath(option, opt, value):
if not exists(value):
raise OptionValueError(
@@ -325,6 +339,7 @@ def check_existing_dirpath(option, opt, value):
else:
return value
+
def check_existing_dirpaths(option, opt, value):
paths = []
for v in value.split(','):
@@ -341,31 +356,36 @@ def check_existing_dirpaths(option, opt, value):
values.append(v)
return values
+
def check_new_filepath(option, opt, value):
if exists(value):
if isdir(value):
raise OptionValueError(
- "option %s: output file exists and it is a directory: %r" %(opt,
- value))
+ "option %s: output file exists and it is a directory: %r" %
+ (opt, value))
return value
-
+
+
def check_new_dirpath(option, opt, value):
if exists(value):
if isfile(value):
raise OptionValueError(
- "option %s: output directory exists and it is a file: %r" %(opt,
- value))
+ "option %s: output directory exists and it is a file: %r" %
+ (opt, value))
return value
-
+
+
def check_existing_path(option, opt, value):
if not exists(value):
raise OptionValueError(
"option %s: path does not exist: %r" % (opt, value))
return value
-
+
+
def check_new_path(option, opt, value):
return value
+
def check_multiple_choice(option, opt, value):
values = value.split(option.split_char)
for v in values:
@@ -376,6 +396,7 @@ def check_multiple_choice(option, opt, value):
% (opt, v, choices))
return values
+
def check_blast_db(option, opt, value):
db_dir, db_name = split(abspath(value))
if not exists(db_dir):
@@ -386,8 +407,9 @@ def check_blast_db(option, opt, value):
"option %s: not a directory: %r" % (opt, db_dir))
return value
+
class PyqiOption(Option):
- ATTRS = Option.ATTRS + ['mchoices','split_char']
+ ATTRS = Option.ATTRS + ['mchoices', 'split_char']
TYPES = Option.TYPES + ("existing_path",
"new_path",
@@ -411,7 +433,7 @@ class PyqiOption(Option):
# for cases where the user passes one or more existing files
# as a comma-separated list - paths are returned as a list
TYPE_CHECKER["existing_filepaths"] = check_existing_filepaths
- # for cases where the user is passing a new path to be
+ # for cases where the user is passing a new path to be
# create (e.g., an output file)
TYPE_CHECKER["new_filepath"] = check_new_filepath
# for cases where the user is passing an existing directory
@@ -420,7 +442,7 @@ class PyqiOption(Option):
# for cases where the user passes one or more existing directories
# as a comma-separated list - paths are returned as a list
TYPE_CHECKER["existing_dirpaths"] = check_existing_dirpaths
- # for cases where the user is passing a new directory to be
+ # for cases where the user is passing a new directory to be
# create (e.g., an output dir which will contain many result files)
TYPE_CHECKER["new_dirpath"] = check_new_dirpath
# for cases where the user is passing one or more values
@@ -435,7 +457,8 @@ def _check_multiple_choice(self):
if self.type == "multiple_choice":
if self.mchoices is None:
raise OptionError(
- "must supply a list of mchoices for type '%s'" % self.type, self)
+ "must supply a list of mchoices for type '%s'" %
+ self.type, self)
elif type(self.mchoices) not in (types.TupleType, types.ListType):
raise OptionError(
"choices must be a list of strings ('%s' supplied)"
diff --git a/pyqi/core/interfaces/optparse/input_handler.py b/pyqi/core/interfaces/optparse/input_handler.py
index 69f116f..a52e778 100644
--- a/pyqi/core/interfaces/optparse/input_handler.py
+++ b/pyqi/core/interfaces/optparse/input_handler.py
@@ -17,12 +17,14 @@
__credits__ = ["Daniel McDonald", "Greg Caporaso", "Doug Wendel",
"Jai Ram Rideout"]
+
def command_handler(option_value):
"""Dynamically load a Python object from a module and return an instance"""
- module, klass = option_value.rsplit('.',1)
+ module, klass = option_value.rsplit('.', 1)
mod = __import__(module, fromlist=[klass])
return getattr(mod, klass)()
+
def string_list_handler(option_value=None):
"""Split a comma-separated string into a list of strings."""
result = None
@@ -30,6 +32,7 @@ def string_list_handler(option_value=None):
result = option_value.split(',')
return result
+
def file_reading_handler(option_value=None):
"""Open a filepath for reading."""
result = None
@@ -37,6 +40,7 @@ def file_reading_handler(option_value=None):
result = open(option_value, 'U')
return result
+
def load_file_lines(option_value):
"""Return a list of strings, one per line in the file.
@@ -45,6 +49,7 @@ def load_file_lines(option_value):
with open(option_value, 'U') as f:
return [line.strip() for line in f]
+
def load_file_contents(option_value):
"""Return the contents of a file as a single string."""
with open(option_value, 'U') as f:
diff --git a/pyqi/core/interfaces/optparse/output_handler.py b/pyqi/core/interfaces/optparse/output_handler.py
index 94d7075..8c39f47 100644
--- a/pyqi/core/interfaces/optparse/output_handler.py
+++ b/pyqi/core/interfaces/optparse/output_handler.py
@@ -26,9 +26,10 @@
from pyqi.core.exception import IncompetentDeveloperError
import os
+
def write_string(result_key, data, option_value=None):
"""Write a string to a file.
-
+
A newline will be added to the end of the file.
"""
if option_value is None:
@@ -42,9 +43,10 @@ def write_string(result_key, data, option_value=None):
f.write(data)
f.write('\n')
+
def write_list_of_strings(result_key, data, option_value=None):
"""Write a list of strings to a file, one per line.
-
+
A newline will be added to the end of the file.
"""
if option_value is None:
@@ -59,6 +61,7 @@ def write_list_of_strings(result_key, data, option_value=None):
f.write(line)
f.write('\n')
+
def print_list_of_strings(result_key, data, option_value=None):
"""Print a list of strings to stdout, one per line.
@@ -67,6 +70,7 @@ def print_list_of_strings(result_key, data, option_value=None):
for line in data:
print line
+
def print_string(result_key, data, option_value=None):
"""Print the string
@@ -74,6 +78,7 @@ def print_string(result_key, data, option_value=None):
print ""
print data
+
def write_or_print_string(result_key, data, option_value=None):
"""Write a string to a file.
@@ -85,6 +90,7 @@ def write_or_print_string(result_key, data, option_value=None):
else:
write_string(result_key, data, option_value)
+
def write_or_print_list_of_strings(result_key, data, option_value=None):
"""Write a list of strings to a file, one per line.
@@ -95,4 +101,3 @@ def write_or_print_list_of_strings(result_key, data, option_value=None):
print_list_of_strings(result_key, data, option_value)
else:
write_list_of_strings(result_key, data, option_value)
-
diff --git a/pyqi/core/log.py b/pyqi/core/log.py
index 80d1c24..5d8b70e 100644
--- a/pyqi/core/log.py
+++ b/pyqi/core/log.py
@@ -15,10 +15,13 @@
__credits__ = ["Greg Caporaso", "Daniel McDonald", "Doug Wendel",
"Jai Ram Rideout"]
+
class InvalidLoggerError(Exception):
pass
+
class Logger(object):
+
"""Abstract logging interface"""
DEBUG = 'DEBUG'
INFO = 'INFO'
@@ -47,13 +50,13 @@ def fatal(self, msg):
def _debug(self, msg):
raise NotImplementedError("All subclasses must implement debug.")
-
+
def _info(self, msg):
raise NotImplementedError("All subclasses must implement info.")
-
+
def _warn(self, msg):
raise NotImplementedError("All subclasses must implement warn.")
-
+
def _fatal(self, msg):
raise NotImplementedError("All subclasses must implement fatal.")
@@ -69,19 +72,28 @@ def _format_line(self, level, msg):
"""Construct a logging line"""
return '%s %s %s' % (self._get_timestamp(), level, msg)
+
class NullLogger(Logger):
+
"""Ignore log messages"""
+
def _debug(self, msg):
pass
+
def _info(self, msg):
pass
+
def _warn(self, msg):
pass
+
def _fatal(self, msg):
pass
+
class StdErrLogger(Logger):
+
"""Log messages directly to ``stderr``"""
+
def _debug(self, msg):
stderr.write(self._format_line(self.DEBUG, msg) + '\n')
diff --git a/pyqi/interfaces/html/config/make_bash_completion.py b/pyqi/interfaces/html/config/make_bash_completion.py
index 93d208d..880d97c 100644
--- a/pyqi/interfaces/html/config/make_bash_completion.py
+++ b/pyqi/interfaces/html/config/make_bash_completion.py
@@ -13,7 +13,7 @@
from pyqi.core.interfaces.html import (HTMLInputOption, HTMLDownload, HTMLPage)
from pyqi.core.interfaces.html.output_handler import newline_list_of_strings
from pyqi.core.command import (make_command_in_collection_lookup_f,
- make_command_out_collection_lookup_f)
+ make_command_out_collection_lookup_f)
from pyqi.commands.make_bash_completion import CommandConstructor
cmd_in_lookup = make_command_in_collection_lookup_f(CommandConstructor)
@@ -23,12 +23,11 @@
HTMLInputOption(Parameter=cmd_in_lookup('command_config_module')),
HTMLInputOption(Parameter=cmd_in_lookup('driver_name')),
HTMLInputOption(Parameter=None,
- Name='download-file',
- Required=True,
- Help='The name of the bash completion script to download. (e.g. my_file)')
+ Name='download-file',
+ Required=True,
+ Help='The name of the bash completion script to download. (e.g. my_file)')
]
-outputs = [ HTMLDownload(Parameter=cmd_out_lookup('result'),
- FilenameLookup='download-file',
- FileExtension='.sh') ]
-
+outputs = [HTMLDownload(Parameter=cmd_out_lookup('result'),
+ FilenameLookup='download-file',
+ FileExtension='.sh')]
diff --git a/pyqi/interfaces/html/config/make_command.py b/pyqi/interfaces/html/config/make_command.py
index a1ac0ec..eb670c5 100644
--- a/pyqi/interfaces/html/config/make_command.py
+++ b/pyqi/interfaces/html/config/make_command.py
@@ -14,7 +14,7 @@
from pyqi.core.interfaces.optparse.input_handler import string_list_handler
from pyqi.core.interfaces.html.output_handler import newline_list_of_strings
from pyqi.core.command import (make_command_in_collection_lookup_f,
- make_command_out_collection_lookup_f)
+ make_command_out_collection_lookup_f)
from pyqi.commands.make_command import CommandConstructor
cmd_in_lookup = make_command_in_collection_lookup_f(CommandConstructor)
@@ -26,25 +26,26 @@
HTMLInputOption(Parameter=cmd_in_lookup('email')),
HTMLInputOption(Parameter=cmd_in_lookup('license')),
HTMLInputOption(Parameter=cmd_in_lookup('copyright')),
- HTMLInputOption(Parameter=cmd_in_lookup('version'), Name='command-version'),
+ HTMLInputOption(Parameter=cmd_in_lookup('version'),
+ Name='command-version'),
HTMLInputOption(Parameter=cmd_in_lookup('credits'),
- Handler=string_list_handler,
- Help='comma-separated list of other authors'),
+ Handler=string_list_handler,
+ Help='comma-separated list of other authors'),
HTMLInputOption(Parameter=cmd_in_lookup('test_code'),
- Type=bool,
- Help='Should a stubbed out python test file be generated instead'),
+ Type=bool,
+ Help='Should a stubbed out python test file be generated instead'),
HTMLInputOption(Parameter=None,
- Name='download-file',
- Required=True,
- Help='The name of the file to download which contains generated Python code. (e.g. my_command)')
+ Name='download-file',
+ Required=True,
+ Help='The name of the file to download which contains generated Python code. (e.g. my_command)')
]
-outputs = [ HTMLDownload(Parameter=cmd_out_lookup('result'),
- Handler=newline_list_of_strings,
- FilenameLookup='download-file',
- FileExtension='.py') ]
+outputs = [HTMLDownload(Parameter=cmd_out_lookup('result'),
+ Handler=newline_list_of_strings,
+ FilenameLookup='download-file',
+ FileExtension='.py')]
-#Comment out the above and uncomment the below for an example of a page.
+# Comment out the above and uncomment the below for an example of a page.
# [ HTMLPage(Parameter=cmd_out_lookup('result'),
# Handler=newline_list_of_strings) ]
diff --git a/pyqi/interfaces/html/config/make_optparse.py b/pyqi/interfaces/html/config/make_optparse.py
index 95eeaea..25432b3 100644
--- a/pyqi/interfaces/html/config/make_optparse.py
+++ b/pyqi/interfaces/html/config/make_optparse.py
@@ -15,32 +15,32 @@
from pyqi.core.interfaces.optparse.input_handler import string_list_handler
from pyqi.core.interfaces.html.output_handler import newline_list_of_strings
from pyqi.core.command import (make_command_in_collection_lookup_f,
- make_command_out_collection_lookup_f)
+ make_command_out_collection_lookup_f)
from pyqi.commands.make_optparse import CommandConstructor
cmd_in_lookup = make_command_in_collection_lookup_f(CommandConstructor)
cmd_out_lookup = make_command_out_collection_lookup_f(CommandConstructor)
inputs = [
- HTMLInputOption(Parameter=cmd_in_lookup('command'),
+ HTMLInputOption(Parameter=cmd_in_lookup('command'),
Handler=command_handler),
HTMLInputOption(Parameter=cmd_in_lookup('command_module')),
HTMLInputOption(Parameter=cmd_in_lookup('author')),
HTMLInputOption(Parameter=cmd_in_lookup('email')),
HTMLInputOption(Parameter=cmd_in_lookup('license')),
HTMLInputOption(Parameter=cmd_in_lookup('copyright')),
- HTMLInputOption(Parameter=cmd_in_lookup('version'), Name='command-version'),
+ HTMLInputOption(Parameter=cmd_in_lookup('version'),
+ Name='command-version'),
HTMLInputOption(Parameter=cmd_in_lookup('credits'),
- Handler=string_list_handler,
- Help='comma-separated list of other authors'),
+ Handler=string_list_handler,
+ Help='comma-separated list of other authors'),
HTMLInputOption(Parameter=None,
- Name='download-file',
- Required=True,
- Help='The name of the file to download which contains generated Python code. (e.g. my_optparse_config)')
+ Name='download-file',
+ Required=True,
+ Help='The name of the file to download which contains generated Python code. (e.g. my_optparse_config)')
]
-outputs = [ HTMLDownload(Parameter=cmd_out_lookup('result'),
- Handler=newline_list_of_strings,
- FilenameLookup='download-file',
- FileExtension='.py') ]
-
+outputs = [HTMLDownload(Parameter=cmd_out_lookup('result'),
+ Handler=newline_list_of_strings,
+ FilenameLookup='download-file',
+ FileExtension='.py')]
diff --git a/pyqi/interfaces/optparse/config/make_bash_completion.py b/pyqi/interfaces/optparse/config/make_bash_completion.py
index 39a7390..a8113b2 100644
--- a/pyqi/interfaces/optparse/config/make_bash_completion.py
+++ b/pyqi/interfaces/optparse/config/make_bash_completion.py
@@ -9,14 +9,14 @@
#-----------------------------------------------------------------------------
__credits__ = ["Daniel McDonald", "Jai Ram Rideout", "Doug Wendel",
- "Greg Caporaso"]
+ "Greg Caporaso"]
from pyqi.core.interfaces.optparse import (OptparseOption,
OptparseUsageExample,
OptparseResult)
from pyqi.core.interfaces.optparse.output_handler import write_string
from pyqi.core.command import (make_command_in_collection_lookup_f,
- make_command_out_collection_lookup_f)
+ make_command_out_collection_lookup_f)
from pyqi.commands.make_bash_completion import CommandConstructor
cmd_in_lookup = make_command_in_collection_lookup_f(CommandConstructor)
diff --git a/pyqi/interfaces/optparse/config/make_command.py b/pyqi/interfaces/optparse/config/make_command.py
index ae17928..49fa86a 100644
--- a/pyqi/interfaces/optparse/config/make_command.py
+++ b/pyqi/interfaces/optparse/config/make_command.py
@@ -55,7 +55,7 @@
]
outputs = [
- ### InputName is used to tie this output to output-fp, which is an input...
+ # InputName is used to tie this output to output-fp, which is an input...
OptparseResult(Parameter=cmd_out_lookup('result'),
Handler=write_list_of_strings,
InputName='output-fp')
diff --git a/pyqi/interfaces/optparse/config/make_optparse.py b/pyqi/interfaces/optparse/config/make_optparse.py
index 2cad401..cbfed99 100644
--- a/pyqi/interfaces/optparse/config/make_optparse.py
+++ b/pyqi/interfaces/optparse/config/make_optparse.py
@@ -12,13 +12,14 @@
"Jai Ram Rideout"]
from pyqi.core.command import Command
-from pyqi.core.interfaces.optparse import (OptparseOption, OptparseUsageExample,
+from pyqi.core.interfaces.optparse import (
+ OptparseOption, OptparseUsageExample,
OptparseResult)
from pyqi.core.interfaces.optparse.input_handler import (command_handler,
string_list_handler)
from pyqi.core.interfaces.optparse.output_handler import write_list_of_strings
from pyqi.core.command import (make_command_in_collection_lookup_f,
- make_command_out_collection_lookup_f)
+ make_command_out_collection_lookup_f)
from pyqi.commands.make_optparse import CommandConstructor
cmdin_lookup = make_command_in_collection_lookup_f(CommandConstructor)
@@ -28,9 +29,10 @@
OptparseUsageExample(ShortDesc="Create an optparse config template",
LongDesc="Construct the beginning of an optparse configuration file based on the Parameters required by the Command.",
Ex='%prog -c pyqi.commands.make_optparse.MakeOptparse -m pyqi.commands.make_optparse -a "some author" --copyright "Copyright 2013, The pyqi project" -e "foo@bar.com" -l BSD --config-version "0.1" --credits "someone else","and another person" -o pyqi/interfaces/optparse/config/make_optparse.py'),
- OptparseUsageExample(ShortDesc="Create a different optparse config template",
- LongDesc="Construct the beginning of an optparse configuration file based on the Parameters required by the Command. This command corresponds to the pyqi tutorial example where a sequence_collection_summarizer command line interface is created for a SequenceCollectionSummarizer Command.",
- Ex='%prog -c sequence_collection_summarizer.SequenceCollectionSummarizer -m sequence_collection_summarizer -a "Greg Caporaso" --copyright "Copyright 2013, Greg Caporaso" -e "gregcaporaso@gmail.com" -l BSD --config-version 0.0.1 -o summarize_sequence_collection.py')
+ OptparseUsageExample(
+ ShortDesc="Create a different optparse config template",
+ LongDesc="Construct the beginning of an optparse configuration file based on the Parameters required by the Command. This command corresponds to the pyqi tutorial example where a sequence_collection_summarizer command line interface is created for a SequenceCollectionSummarizer Command.",
+ Ex='%prog -c sequence_collection_summarizer.SequenceCollectionSummarizer -m sequence_collection_summarizer -a "Greg Caporaso" --copyright "Copyright 2013, Greg Caporaso" -e "gregcaporaso@gmail.com" -l BSD --config-version 0.0.1 -o summarize_sequence_collection.py')
]
inputs = [
diff --git a/pyqi/interfaces/optparse/config/serve_html_interface.py b/pyqi/interfaces/optparse/config/serve_html_interface.py
index 4133f8c..f5df4ba 100644
--- a/pyqi/interfaces/optparse/config/serve_html_interface.py
+++ b/pyqi/interfaces/optparse/config/serve_html_interface.py
@@ -22,7 +22,6 @@
cmdout_lookup = make_command_out_collection_lookup_f(CommandConstructor)
-
usage_examples = [
OptparseUsageExample(ShortDesc="Start html interface",
LongDesc="Starts an html interface server on the specified --port and --interface-module",
diff --git a/pyqi/interfaces/optparse/input_handler.py b/pyqi/interfaces/optparse/input_handler.py
index e5b0061..d1db5ff 100644
--- a/pyqi/interfaces/optparse/input_handler.py
+++ b/pyqi/interfaces/optparse/input_handler.py
@@ -12,4 +12,4 @@
__credits__ = ["Greg Caporaso", "Daniel McDonald", "Doug Wendel",
"Jai Ram Rideout"]
-## Store project/inteface specific input handlers here
+# Store project/inteface specific input handlers here
diff --git a/pyqi/interfaces/optparse/output_handler.py b/pyqi/interfaces/optparse/output_handler.py
index 7cad4b9..74cb9f3 100644
--- a/pyqi/interfaces/optparse/output_handler.py
+++ b/pyqi/interfaces/optparse/output_handler.py
@@ -12,4 +12,4 @@
__credits__ = ["Greg Caporaso", "Daniel McDonald", "Doug Wendel",
"Jai Ram Rideout"]
-## Store project/inteface specific output handlers here
+# Store project/inteface specific output handlers here
diff --git a/pyqi/util.py b/pyqi/util.py
index b82bad6..0dcbc3d 100644
--- a/pyqi/util.py
+++ b/pyqi/util.py
@@ -13,23 +13,25 @@
__credits__ = ["Greg Caporaso", "Jai Ram Rideout"]
import importlib
+import sys
from os import remove
from os.path import split, splitext
-import sys
-from subprocess import Popen, PIPE, STDOUT
+from subprocess import Popen, PIPE
+
from pyqi.core.log import StdErrLogger
from pyqi.core.exception import MissingVersionInfoError
+
def pyqi_system_call(cmd, shell=True, dry_run=False):
"""Call cmd and return (stdout, stderr, return_value).
- cmd: can be either a string containing the command to be run, or a
+ cmd: can be either a string containing the command to be run, or a
sequence of strings that are the tokens of the command.
- shell: value passed directly to Popen (default: True). See Python's
+ shell: value passed directly to Popen (default: True). See Python's
subprocess.Popen for a description of the shell parameter and how cmd
is interpreted differently based on its value.
dry_run: if True, print cmd and return ("", "", 0) (default: False)
-
+
This function is ported from QIIME (http://www.qiime.org), previously
named qiime_system_call. QIIME is a GPL project, but we obtained permission
from the authors of this function to port it to pyqi (and keep it under
@@ -48,12 +50,13 @@ def pyqi_system_call(cmd, shell=True, dry_run=False):
universal_newlines=True,
stdout=PIPE,
stderr=PIPE)
- # communicate pulls all stdout/stderr from the PIPEs to
+ # communicate pulls all stdout/stderr from the PIPEs to
# avoid blocking -- don't remove this line!
stdout, stderr = proc.communicate()
return_value = proc.returncode
return stdout, stderr, return_value
+
def remove_files(list_of_filepaths, error_on_missing=True):
"""Remove list of filepaths, optionally raising an error if any are missing
@@ -69,8 +72,9 @@ def remove_files(list_of_filepaths, error_on_missing=True):
missing.append(fp)
if error_on_missing and missing:
- raise OSError, "Some filepaths were not accessible: %s" % '\t'.join(
- missing)
+ raise OSError("Some filepaths were not accessible: %s" % '\t'.join(
+ missing))
+
def old_to_new_command(driver_name, project_title, local_argv):
"""Deprecate an old-style script.
@@ -103,6 +107,7 @@ def old_to_new_command(driver_name, project_title, local_argv):
return result_retval
+
def get_version_string(module_str):
"""Returns the version string found in the top-level module.
@@ -124,6 +129,7 @@ def get_version_string(module_str):
version_string = top_level_module.__version__
except AttributeError:
raise MissingVersionInfoError("Module '%s' does not have the "
- "__version__ attribute." % top_level_name)
+ "__version__ attribute."
+ % top_level_name)
return version_string
diff --git a/setup.py b/setup.py
index 0667db3..b412c33 100644
--- a/setup.py
+++ b/setup.py
@@ -57,7 +57,7 @@
long_description = """pyqi (canonically pronounced pie chee) is a Python framework designed to support wrapping general commands in multiple types of interfaces, including at the command line, HTML, and API levels."""
setup(name='pyqi',
- cmdclass={'build_py':build_py},
+ cmdclass={'build_py': build_py},
version=__version__,
license=__license__,
description='pyqi: expose your interface',
@@ -81,9 +81,9 @@
],
scripts=glob('scripts/pyqi*'),
install_requires=[],
- extras_require={'test':["nose >= 0.10.1",
- "tox >= 1.6.1"],
- 'doc':"Sphinx >= 0.3"
- },
+ extras_require={'test': ["nose >= 0.10.1",
+ "tox >= 1.6.1"],
+ 'doc': "Sphinx >= 0.3"
+ },
classifiers=classifiers
)
diff --git a/tests/test_commands/test_code_header_generator.py b/tests/test_commands/test_code_header_generator.py
index b1c91e7..d6c591c 100644
--- a/tests/test_commands/test_code_header_generator.py
+++ b/tests/test_commands/test_code_header_generator.py
@@ -14,7 +14,9 @@
from unittest import TestCase, main
from pyqi.commands.code_header_generator import CodeHeaderGenerator
+
class CodeHeaderGeneratorTests(TestCase):
+
def setUp(self):
"""Set up a CodeHeaderGenerator instance to use in the tests."""
self.cmd = CodeHeaderGenerator()
diff --git a/tests/test_commands/test_make_bash_completion.py b/tests/test_commands/test_make_bash_completion.py
index aad1c08..2788ee8 100644
--- a/tests/test_commands/test_make_bash_completion.py
+++ b/tests/test_commands/test_make_bash_completion.py
@@ -21,7 +21,9 @@
__credits__ = ["Daniel McDonald", "Jai Ram Rideout", "Doug Wendel",
"Greg Caporaso"]
+
class BashCompletionTests(TestCase):
+
def setUp(self):
"""Set up data for unit tests."""
self.cmd = BashCompletion()
@@ -72,8 +74,8 @@ def test_get_cfg_module(self):
self.assertEqual(_get_cfg_module('pyqi'), pyqi)
def test_run(self):
- params = {'command_config_module':self.temp_module_name,
- 'driver_name':'pyqi'}
+ params = {'command_config_module': self.temp_module_name,
+ 'driver_name': 'pyqi'}
obs = self.cmd(**params)
self.assertEqual(obs.keys(), ['result'])
self.assertEqual(obs['result'], outputandstuff)
@@ -89,7 +91,7 @@ def test_run(self):
if [ $COMP_CWORD -gt 1 ]; then
prev=${COMP_WORDS[1]}
- fi
+ fi
if [ $COMP_CWORD -eq 1 ]; then
COMPREPLY=( $(compgen -W "make-bash-completion make-optparse" -- $cur) )
diff --git a/tests/test_commands/test_make_command.py b/tests/test_commands/test_make_command.py
index 94a680c..85e8975 100644
--- a/tests/test_commands/test_make_command.py
+++ b/tests/test_commands/test_make_command.py
@@ -14,7 +14,9 @@
from unittest import TestCase, main
from pyqi.commands.make_command import MakeCommand
+
class MakeCommandTests(TestCase):
+
def setUp(self):
"""Set up a MakeCommand instance to use in the tests."""
self.cmd = MakeCommand()
@@ -52,7 +54,7 @@ def test_run_test_code_generation(self):
__maintainer__ = "bob"
__email__ = "bob@bob.bob"
-from pyqi.core.command import (Command, CommandIn, CommandOut,
+from pyqi.core.command import (Command, CommandIn, CommandOut,
ParameterCollection)
class Test(Command):
diff --git a/tests/test_commands/test_make_optparse.py b/tests/test_commands/test_make_optparse.py
index d36dc2f..7cd0604 100644
--- a/tests/test_commands/test_make_optparse.py
+++ b/tests/test_commands/test_make_optparse.py
@@ -14,15 +14,17 @@
from unittest import TestCase, main
__credits__ = ["Daniel McDonald", "Jai Ram Rideout", "Doug Wendel",
- "Greg Caporaso"]
+ "Greg Caporaso"]
+
class MakeOptparseTests(TestCase):
+
def setUp(self):
self.cmd = MakeOptparse()
def test_run(self):
exp = win_text
-
+
pc = CommandIn(Name='DUN', Required=True, DataType=str, Description="")
bool_param = CommandIn(Name='imabool', DataType=bool,
Description='zero or one', Required=False)
@@ -31,17 +33,17 @@ class stubby:
CommandIns = ParameterCollection([pc, bool_param])
CommandOuts = ParameterCollection([])
- obs = self.cmd(**{'command_module':'foobar',
- 'command':stubby(),
+ obs = self.cmd(**{'command_module': 'foobar',
+ 'command': stubby(),
'author': 'bob',
'email': 'bob@bob.bob',
'license': 'very permissive license',
'copyright': 'what\'s that?',
'version': '1.0'
- })
-
+ })
+
self.assertEqual(obs['result'], exp.splitlines())
-
+
win_text = """#!/usr/bin/env python
from __future__ import division
@@ -136,7 +138,7 @@ class stubby:
# # value will be made available to Handler. This name
# # can be either an underscored or dashed version of the
# # option name (e.g., 'output_fp' or 'output-fp')
- # InputName='output-fp'),
+ # InputName='output-fp'),
#
# An example option that does not map to a CommandIn.
# OptparseResult(Parameter=cmd_out_lookup('some_other_result'),
diff --git a/tests/test_core/test_command.py b/tests/test_core/test_command.py
index b2e70e2..7c1b337 100644
--- a/tests/test_core/test_command.py
+++ b/tests/test_core/test_command.py
@@ -13,19 +13,23 @@
"Jai Ram Rideout"]
from unittest import TestCase, main
-from pyqi.core.command import CommandIn, CommandOut, ParameterCollection, Command
-from pyqi.core.exception import (IncompetentDeveloperError,
- UnknownParameterError,
+from pyqi.core.command import (CommandIn, CommandOut, ParameterCollection,
+ Command)
+from pyqi.core.exception import (IncompetentDeveloperError,
+ UnknownParameterError,
MissingParameterError)
+
class CommandTests(TestCase):
+
def setUp(self):
class stubby(Command):
CommandIns = ParameterCollection([
- CommandIn('a',int,'', Required=True),
- CommandIn('b',int,'', Required=False, Default=5),
- CommandIn('c',int,'', Required=False, Default=10,
- ValidateValue=lambda x: x == 10)])
+ CommandIn('a', int, '', Required=True),
+ CommandIn('b', int, '', Required=False, Default=5),
+ CommandIn('c', int, '', Required=False, Default=10,
+ ValidateValue=lambda x: x == 10)])
+
def run(self, **kwargs):
return {}
self.stubby = stubby
@@ -45,39 +49,42 @@ class foo(Command):
Required=True),
CommandIn('b', str, 'help2',
Required=False)])
+
def run(self, **kwargs):
return {}
obs = foo()
self.assertEqual(len(obs.Parameters), 2)
- self.assertEqual(obs.run(bar={'a':10}), {})
+ self.assertEqual(obs.run(bar={'a': 10}), {})
def test_validate_kwargs(self):
stub = self.stubby()
- kwargs = {'a':10, 'b':20}
-
+ kwargs = {'a': 10, 'b': 20}
+
# should work
stub._validate_kwargs(kwargs)
- kwargs = {'b':20}
+ kwargs = {'b': 20}
self.assertRaises(MissingParameterError, stub._validate_kwargs, kwargs)
-
- kwargs = {'a':10, 'b':20, 'c':10}
+
+ kwargs = {'a': 10, 'b': 20, 'c': 10}
stub._validate_kwargs(kwargs)
- kwargs = {'a':10, 'b':20, 'c':20}
+ kwargs = {'a': 10, 'b': 20, 'c': 20}
self.assertRaises(ValueError, stub._validate_kwargs, kwargs)
def test_set_defaults(self):
stub = self.stubby()
- kwargs = {'a':10}
- exp = {'a':10,'b':5,'c':10}
-
+ kwargs = {'a': 10}
+ exp = {'a': 10, 'b': 5, 'c': 10}
+
stub._set_defaults(kwargs)
self.assertEqual(kwargs, exp)
+
class ParameterTests(TestCase):
+
def test_init(self):
"""Jog the init"""
obj = CommandIn('a', str, 'help', Required=False)
@@ -90,10 +97,12 @@ def test_init(self):
self.assertRaises(IncompetentDeveloperError, CommandIn, 'a', str,
'help', True, 'x')
+
class ParameterCollectionTests(TestCase):
+
def setUp(self):
- self.pc = ParameterCollection([CommandIn('foo',str, 'help')])
-
+ self.pc = ParameterCollection([CommandIn('foo', str, 'help')])
+
def test_init(self):
"""Jog the init"""
params = [CommandIn('a', str, 'help', Required=False),
@@ -111,7 +120,8 @@ def test_init(self):
def test_getitem(self):
self.assertRaises(UnknownParameterError, self.pc.__getitem__, 'bar')
- self.assertEqual(self.pc['foo'].Name, 'foo') # make sure we can getitem
+ # make sure we can getitem
+ self.assertEqual(self.pc['foo'].Name, 'foo')
def test_setitem(self):
self.assertRaises(TypeError, self.pc.__setitem__, 'bar', 10)
diff --git a/tests/test_core/test_interface.py b/tests/test_core/test_interface.py
index 63e05c0..c98e478 100644
--- a/tests/test_core/test_interface.py
+++ b/tests/test_core/test_interface.py
@@ -16,10 +16,12 @@
from pyqi.core.interface import get_command_names, get_command_config
import pyqi.interfaces.optparse.config.make_bash_completion
+
class TopLevelTests(TestCase):
+
def test_get_command_names(self):
"""Test that command names are returned from a config directory."""
- exp = ['make-bash-completion', 'make-command', 'make-optparse',
+ exp = ['make-bash-completion', 'make-command', 'make-optparse',
'make-release', 'serve-html-interface']
obs = get_command_names('pyqi.interfaces.optparse.config')
self.assertEqual(obs, exp)
@@ -31,14 +33,14 @@ def test_get_command_names(self):
def test_get_command_config(self):
"""Test successful and unsuccessful module loading."""
cmd_cfg, error_msg = get_command_config(
- 'pyqi.interfaces.optparse.config', 'make_bash_completion')
+ 'pyqi.interfaces.optparse.config', 'make_bash_completion')
self.assertEqual(cmd_cfg,
pyqi.interfaces.optparse.config.make_bash_completion)
self.assertEqual(error_msg, None)
cmd_cfg, error_msg = get_command_config(
- 'hopefully.nonexistent.python.module', 'umm',
- exit_on_failure=False)
+ 'hopefully.nonexistent.python.module', 'umm',
+ exit_on_failure=False)
self.assertEqual(cmd_cfg, None)
self.assertEqual(error_msg, 'No module named hopefully.nonexistent.'
'python.module.umm')
diff --git a/tests/test_core/test_interfaces/test_html/test_input_handler.py b/tests/test_core/test_interfaces/test_html/test_input_handler.py
index e22dafe..5b138d2 100644
--- a/tests/test_core/test_interfaces/test_html/test_input_handler.py
+++ b/tests/test_core/test_interfaces/test_html/test_input_handler.py
@@ -14,16 +14,18 @@
from unittest import TestCase, main
from pyqi.core.exception import IncompetentDeveloperError
from pyqi.core.interfaces.html.input_handler import (load_file_lines,
- load_file_contents)
+ load_file_contents)
+
class HTMLInputHandlerTests(TestCase):
+
def setUp(self):
self.file_like_object = StringIO()
- #Note the whitespace, this tests strip()
+ # Note the whitespace, this tests strip()
self.file_like_object.write("This is line 1\n")
self.file_like_object.write(" This is line 2\n")
self.file_like_object.write("This is line 3 \n")
- #Place it at the beginning of the file again
+ # Place it at the beginning of the file again
self.file_like_object.seek(0)
def tearDown(self):
@@ -32,21 +34,24 @@ def tearDown(self):
def test_load_file_lines(self):
"""Correctly returns file lines as a list of strings"""
# can't load a string, etc...
- self.assertRaises(IncompetentDeveloperError, load_file_lines, 'This is not a file')
+ self.assertRaises(IncompetentDeveloperError,
+ load_file_lines, 'This is not a file')
result = load_file_lines(self.file_like_object)
- self.assertEqual(result,
- ["This is line 1",
+ self.assertEqual(result,
+ ["This is line 1",
"This is line 2",
"This is line 3"])
def test_load_file_contents(self):
"""Correctly returns file contents"""
# can't load a string, etc...
- self.assertRaises(IncompetentDeveloperError, load_file_contents, 'This is not a file')
+ self.assertRaises(IncompetentDeveloperError,
+ load_file_contents, 'This is not a file')
result = load_file_contents(self.file_like_object)
- #Note the whitespace
- self.assertEqual(result, "This is line 1\n This is line 2\nThis is line 3 \n")
+ # Note the whitespace
+ self.assertEqual(
+ result, "This is line 1\n This is line 2\nThis is line 3 \n")
if __name__ == '__main__':
main()
diff --git a/tests/test_core/test_interfaces/test_html/test_output_handler.py b/tests/test_core/test_interfaces/test_html/test_output_handler.py
index abb59d1..dcdee86 100644
--- a/tests/test_core/test_interfaces/test_html/test_output_handler.py
+++ b/tests/test_core/test_interfaces/test_html/test_output_handler.py
@@ -12,18 +12,19 @@
from unittest import TestCase, main
from pyqi.core.interfaces.html.output_handler import (newline_list_of_strings,
- html_list_of_strings)
+ html_list_of_strings)
+
class HTMLOutputHandlerTests(TestCase):
def test_newline_list_of_strings(self):
"""Correctly returns a list of strings to delimited by '\n'."""
- result = newline_list_of_strings('foo', ['bar','bay','baz'])
+ result = newline_list_of_strings('foo', ['bar', 'bay', 'baz'])
self.assertEqual(result, 'bar\nbay\nbaz')
def test_html_list_of_strings(self):
"""Correctly returns a list of strings delimited by ' '."""
- result = html_list_of_strings('foo', ['bar','bay','baz'])
+ result = html_list_of_strings('foo', ['bar', 'bay', 'baz'])
self.assertEqual(result, 'bar bay baz')
if __name__ == '__main__':
diff --git a/tests/test_core/test_interfaces/test_optparse/test_init.py b/tests/test_core/test_interfaces/test_optparse/test_init.py
index 9e85823..bc7ea73 100644
--- a/tests/test_core/test_interfaces/test_optparse/test_init.py
+++ b/tests/test_core/test_interfaces/test_optparse/test_init.py
@@ -33,11 +33,14 @@
from os import remove, rmdir
from os.path import commonprefix
+
class OptparseResultTests(TestCase):
# Nothing to test yet
pass
+
class OptparseOptionTests(TestCase):
+
def setUp(self):
p = CommandIn('number', int, 'some int', Required=False, Default=42,
DefaultDescription='forty-two')
@@ -45,7 +48,7 @@ def setUp(self):
self.opt1 = OptparseOption(Parameter=p, Type=int)
# Without associated parameter.
- self.opt2 = OptparseOption(Parameter=None, Type=int, Handler=None,
+ self.opt2 = OptparseOption(Parameter=None, Type=int, Handler=None,
ShortName='n',
Name='number', Required=False,
Help='help!!!')
@@ -68,7 +71,9 @@ def test_str(self):
obs = str(self.opt2)
self.assertEqual(obs, exp)
+
class OptparseUsageExampleTests(TestCase):
+
def test_init(self):
obj = OptparseUsageExample(ShortDesc='a', LongDesc='b', Ex='c')
self.assertEqual(obj.ShortDesc, 'a')
@@ -78,13 +83,16 @@ def test_init(self):
with self.assertRaises(IncompetentDeveloperError):
_ = OptparseUsageExample('a', 'b', Ex=None)
+
def oh(key, data, opt_value=None):
return data * 2
+
class OptparseInterfaceTests(TestCase):
+
def setUp(self):
self.interface = fabulous()
-
+
def test_init(self):
self.assertRaises(IncompetentDeveloperError, OptparseInterface)
@@ -99,7 +107,7 @@ def test_validate_inputs(self):
_ = DuplicateOptionMappings()
def test_input_handler(self):
- obs = self.interface._input_handler(['--c','foo'])
+ obs = self.interface._input_handler(['--c', 'foo'])
self.assertEqual(obs.items(), [('c', 'foo')])
def test_build_usage_lines(self):
@@ -107,21 +115,26 @@ def test_build_usage_lines(self):
self.assertEqual(obs, usage_lines)
def test_output_handler(self):
- results = {'itsaresult':20}
+ results = {'itsaresult': 20}
obs = self.interface._output_handler(results)
- self.assertEqual(obs, {'itsaresult':40})
+ self.assertEqual(obs, {'itsaresult': 40})
+
class GeneralTests(TestCase):
+
def setUp(self):
- self.obj = optparse_factory(ghetto,
- [OptparseUsageExample('a','b','c')],
- [OptparseOption(Type=str,
- Parameter=ghetto.CommandIns['c'],
- ShortName='n')],
- [OptparseResult(Type=str,
- Parameter=ghetto.CommandOuts['itsaresult'],
- Handler=oh)],
- '2.0-dev')
+ self.obj = optparse_factory(
+ ghetto,
+ [OptparseUsageExample('a', 'b', 'c')],
+ [OptparseOption(
+ Type=str,
+ Parameter=ghetto.CommandIns['c'],
+ ShortName='n')],
+ [OptparseResult(
+ Type=str,
+ Parameter=ghetto.CommandOuts['itsaresult'],
+ Handler=oh)],
+ '2.0-dev')
def test_optparse_factory(self):
# exercise it
@@ -131,12 +144,14 @@ def test_optparse_main(self):
# exercise it
_ = optparse_main(self.obj, ['testing', '--c', 'bar'])
+
class ghetto(Command):
CommandIns = ParameterCollection([CommandIn('c', str, 'b')])
CommandOuts = ParameterCollection([CommandOut('itsaresult', str, 'x')])
def run(self, **kwargs):
- return {'itsaresult':10}
+ return {'itsaresult': 10}
+
class fabulous(OptparseInterface):
CommandConstructor = ghetto
@@ -147,31 +162,41 @@ def _get_inputs(self):
ShortName='n')]
def _get_usage_examples(self):
- return [OptparseUsageExample('a','b','c')]
+ return [OptparseUsageExample('a', 'b', 'c')]
def _get_outputs(self):
- return [OptparseResult(Parameter=ghetto.CommandOuts['itsaresult'], Handler=oh)]
+ return [OptparseResult(Parameter=ghetto.CommandOuts['itsaresult'],
+ Handler=oh)]
def _get_version(self):
return '2.0-dev'
# Doesn't have any usage examples...
+
+
class NoUsageExamples(fabulous):
+
def _get_usage_examples(self):
return []
# More than one option mapping to the same Parameter...
+
+
class DuplicateOptionMappings(fabulous):
+
def _get_inputs(self):
return [
OptparseOption(Type=str,
- Parameter=self.CommandConstructor.CommandIns['c'], ShortName='n'),
+ Parameter=self.CommandConstructor.CommandIns[
+ 'c'], ShortName='n'),
OptparseOption(Parameter=self.CommandConstructor.CommandIns['c'],
- Name='i-am-a-duplicate')
+ Name='i-am-a-duplicate')
]
+
class TypeCheckTests(TestCase):
+
def setUp(self):
self._paths_to_clean_up = []
self._dirs_to_clean_up = []
@@ -189,15 +214,15 @@ def test_check_existing_filepath(self):
self.assertEqual(obs, tmp_path)
# Check that raises an error when the file doesn't exists
self.assertRaises(OptionValueError, check_existing_filepath, option,
- '-f', '/hopefully/a/non/existing/file')
+ '-f', '/hopefully/a/non/existing/file')
# Check that raises an error when the path exists and is a directory
tmp_dirpath = mkdtemp()
self._dirs_to_clean_up = [tmp_dirpath]
self.assertRaises(OptionValueError, check_existing_filepath, option,
- '-f', tmp_dirpath)
+ '-f', tmp_dirpath)
def test_check_existing_filepaths(self):
- # Check that returns a list with the paths, in the same order as
+ # Check that returns a list with the paths, in the same order as
# the input comma separated list
tmp_f1, tmp_path1 = mkstemp(prefix='pyqi_tmp_')
tmp_f2, tmp_path2 = mkstemp(prefix='pyqi_tmp_')
@@ -214,17 +239,18 @@ def test_check_existing_filepaths(self):
self.assertEqual(set(obs), set(exp))
# Check that raises an error when the wildcard does not match any file
self.assertRaises(OptionValueError, check_existing_filepaths, option,
- '-f', '/hopefully/a/non/existing/path*')
+ '-f', '/hopefully/a/non/existing/path*')
# Check that raises an error when one of the files does not exist
- value = ",".join([tmp_path1,tmp_path2,'/hopefully/a/non/existing/file'])
+ value = ",".join(
+ [tmp_path1, tmp_path2, '/hopefully/a/non/existing/file'])
self.assertRaises(OptionValueError, check_existing_filepaths, option,
- '-f', value)
+ '-f', value)
# Check that raises an error when one of the paths is a folder
tmp_dirpath = mkdtemp()
self._dirs_to_clean_up = [tmp_dirpath]
value = ",".join([tmp_path1, tmp_path2, tmp_dirpath])
self.assertRaises(OptionValueError, check_existing_filepaths, option,
- '-f', value)
+ '-f', value)
def test_check_existing_dirpath(self):
# Check that returns the correct value when the directory exists
@@ -235,12 +261,12 @@ def test_check_existing_dirpath(self):
self.assertEqual(obs, tmp_dirpath)
# Check that raises an error when the folder doesn't exists
self.assertRaises(OptionValueError, check_existing_dirpath, option,
- '-f', '/hopefully/a/non/existing/directory')
+ '-f', '/hopefully/a/non/existing/directory')
# Check that raises an error when the path exists and is a file
tmp_f, tmp_path = mkstemp()
self._paths_to_clean_up = [tmp_path]
self.assertRaises(OptionValueError, check_existing_dirpath, option,
- '-f', tmp_path)
+ '-f', tmp_path)
def test_check_existing_dirpaths(self):
# Check that returns a list with the paths, in the same order as the
@@ -260,18 +286,18 @@ def test_check_existing_dirpaths(self):
self.assertEqual(set(obs), set(exp))
# Check that raises an error when the wildcard does not match any path
self.assertRaises(OptionValueError, check_existing_dirpaths, option,
- '-f', '/hopefully/a/non/existing/path*')
+ '-f', '/hopefully/a/non/existing/path*')
# Check that raises an error when one of the directories does not exist
value = ",".join([tmp_dirpath1, tmp_dirpath2,
- '/hopefully/a/non/existing/path*'])
+ '/hopefully/a/non/existing/path*'])
self.assertRaises(OptionValueError, check_existing_dirpaths, option,
- '-f', value)
+ '-f', value)
# Check that raises an error when one of the paths is a file
tmp_f, tmp_path = mkstemp()
self._paths_to_clean_up = [tmp_path]
value = ",".join([tmp_dirpath1, tmp_dirpath2, tmp_path])
self.assertRaises(OptionValueError, check_existing_dirpaths, option,
- '-f', value)
+ '-f', value)
def test_check_new_filepath(self):
# Check that it doesn't raise an error if the path does not exist
@@ -288,7 +314,7 @@ def test_check_new_filepath(self):
tmp_dirpath = mkdtemp()
self._dirs_to_clean_up = [tmp_dirpath]
self.assertRaises(OptionValueError, check_new_filepath, option, '-n',
- tmp_dirpath)
+ tmp_dirpath)
def test_check_new_dirpath(self):
# Check that it doesn't raise an error if the path does not exist
@@ -296,7 +322,8 @@ def test_check_new_dirpath(self):
exp = '/hopefully/a/non/existing/dir'
obs = check_new_dirpath(option, '-n', exp)
self.assertEqual(obs, exp)
- # Check that it doesn't raise an error if the path exists and is a directory
+ # Check that it doesn't raise an error if the path exists and is a
+ # directory
tmp_dirpath = mkdtemp()
self._dirs_to_clean_up = [tmp_dirpath]
obs = check_new_dirpath(option, '-n', tmp_dirpath)
@@ -305,7 +332,7 @@ def test_check_new_dirpath(self):
tmp_f, tmp_path = mkstemp()
self._paths_to_clean_up = [tmp_path]
self.assertRaises(OptionValueError, check_new_dirpath, option, '-n',
- tmp_path)
+ tmp_path)
def test_check_existing_path(self):
# Check that it doesn't raise an error if an existing file is passed
@@ -314,14 +341,15 @@ def test_check_existing_path(self):
self._paths_to_clean_up = [tmp_path]
obs = check_existing_path(option, '-p', tmp_path)
self.assertEqual(obs, tmp_path)
- # Check that it doesn't raise an error if an existing directory is passed
+ # Check that it doesn't raise an error if an existing directory is
+ # passed
tmp_dirpath = mkdtemp()
self._dirs_to_clean_up = [tmp_dirpath]
obs = check_existing_path(option, '-p', tmp_dirpath)
self.assertEqual(obs, tmp_dirpath)
# Check that it raises an error if the path doesn't exist
self.assertRaises(OptionValueError, check_existing_path, option, '-n',
- '/hopefully/a/non/existing/path')
+ '/hopefully/a/non/existing/path')
def test_check_new_path(self):
# Really? Too much work...
@@ -333,21 +361,24 @@ def test_check_new_path(self):
def test_check_multiple_choice(self):
# Check that it doesn't raise an error when the value is in the list
option = PyqiOption('-m', '--multiple', type="multiple_choice",
- mchoices=['choice_A', 'choice_B', 'choice_C'])
+ mchoices=['choice_A', 'choice_B', 'choice_C'])
exp = ["choice_B"]
obs = check_multiple_choice(option, '-m', exp[0])
self.assertEqual(obs, exp)
- exp = ["choice_B","choice_C"]
+ exp = ["choice_B", "choice_C"]
obs = check_multiple_choice(option, '-m', ",".join(exp))
self.assertEqual(obs, exp)
# Check that it raises an error when the value is not in the list
- self.assertRaises(OptionValueError, check_multiple_choice, option, '-n',
+ self.assertRaises(
+ OptionValueError, check_multiple_choice, option, '-n',
"choice_not_listed")
- self.assertRaises(OptionValueError, check_multiple_choice, option, '-n',
+ self.assertRaises(
+ OptionValueError, check_multiple_choice, option, '-n',
"choice_A,choice_not_listed")
def test_check_blast_db(self):
- # Check that it doesn't raise an error when a blastdb-like prefix is passed
+ # Check that it doesn't raise an error when a blastdb-like prefix is
+ # passed
option = PyqiOption('-b', '--blast_test', type="blast_db")
tmp_f1, tmp_path1 = mkstemp(prefix='pyqi_tmp_')
tmp_f2, tmp_path2 = mkstemp(prefix='pyqi_tmp_')
@@ -357,7 +388,7 @@ def test_check_blast_db(self):
self.assertEqual(obs, exp)
# Check that raises an error if the base folder does not exist
self.assertRaises(OptionValueError, check_blast_db, option, '-b',
- '/hopefully/a/non/existing/path')
+ '/hopefully/a/non/existing/path')
usage_lines = """usage: %prog [options] {}
@@ -366,7 +397,7 @@ def test_check_blast_db(self):
-Example usage:
+Example usage:
Print help message and exit
%prog -h
diff --git a/tests/test_core/test_interfaces/test_optparse/test_input_handler.py b/tests/test_core/test_interfaces/test_optparse/test_input_handler.py
index 94b6199..6ddf106 100644
--- a/tests/test_core/test_interfaces/test_optparse/test_input_handler.py
+++ b/tests/test_core/test_interfaces/test_optparse/test_input_handler.py
@@ -15,7 +15,9 @@
from pyqi.core.interfaces.optparse.input_handler import command_handler
from pyqi.commands.make_optparse import MakeOptparse
+
class OptparseInputHandlerTests(TestCase):
+
def setUp(self):
pass
diff --git a/tests/test_core/test_interfaces/test_optparse/test_output_handler.py b/tests/test_core/test_interfaces/test_optparse/test_output_handler.py
index c4c9a82..89d1c66 100644
--- a/tests/test_core/test_interfaces/test_optparse/test_output_handler.py
+++ b/tests/test_core/test_interfaces/test_optparse/test_output_handler.py
@@ -17,11 +17,13 @@
from shutil import rmtree
from tempfile import mkdtemp
from unittest import TestCase, main
-from pyqi.core.interfaces.optparse.output_handler import (write_string,
- write_list_of_strings, print_list_of_strings)
+from pyqi.core.interfaces.optparse.output_handler import (
+ write_string, write_list_of_strings, print_list_of_strings)
from pyqi.core.exception import IncompetentDeveloperError
+
class OutputHandlerTests(TestCase):
+
def setUp(self):
self.output_dir = mkdtemp()
self.fp = os.path.join(self.output_dir, 'test_file.txt')
@@ -32,7 +34,7 @@ def tearDown(self):
def test_write_string(self):
"""Correctly writes a string to file."""
# can't write without a path
- self.assertRaises(IncompetentDeveloperError, write_string, 'a','b')
+ self.assertRaises(IncompetentDeveloperError, write_string, 'a', 'b')
write_string('foo', 'bar', self.fp)
with open(self.fp, 'U') as obs_f:
@@ -57,7 +59,7 @@ def test_print_list_of_strings(self):
# Save stdout and replace it with something that will capture the print
# statement. Note: this code was taken from here:
# http://stackoverflow.com/questions/4219717/how-to-assert-output-
- # with-nosetest-unittest-in-python/4220278#4220278
+ # with-nosetest-unittest-in-python/4220278#4220278
saved_stdout = sys.stdout
try:
out = StringIO()
diff --git a/tests/test_util.py b/tests/test_util.py
index 30462b5..2e0285c 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -17,7 +17,9 @@
from pyqi.util import get_version_string
from pyqi.core.exception import MissingVersionInfoError
+
class UtilTests(TestCase):
+
def test_get_version_string(self):
"""Test extracting a version string given a module string."""
exp = pyqi.__version__