diff options
author | Lars Wirzenius <liw@liw.fi> | 2015-04-18 11:18:54 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2015-04-18 15:41:49 +0300 |
commit | a09e1064f58673591093df7f3b709a775c06b866 (patch) | |
tree | 173e13e3a3293e4a1871bb1fa2bb1ad3cd07d938 | |
parent | d3b79bed48895150fb7c33f577716a900fda3833 (diff) | |
download | cliapp-a09e1064f58673591093df7f3b709a775c06b866.tar.gz |
Add running of pylint in 'make check' and fix things
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | cliapp/__init__.py | 18 | ||||
-rw-r--r-- | cliapp/app.py | 78 | ||||
-rw-r--r-- | cliapp/app_tests.py | 43 | ||||
-rw-r--r-- | cliapp/hook_tests.py | 4 | ||||
-rw-r--r-- | cliapp/plugin.py | 7 | ||||
-rw-r--r-- | cliapp/plugin_tests.py | 26 | ||||
-rw-r--r-- | cliapp/pluginmgr.py | 8 | ||||
-rw-r--r-- | cliapp/pluginmgr_tests.py | 14 | ||||
-rw-r--r-- | cliapp/runcmd.py | 13 | ||||
-rw-r--r-- | cliapp/runcmd_tests.py | 34 | ||||
-rw-r--r-- | cliapp/settings.py | 66 | ||||
-rw-r--r-- | cliapp/settings_tests.py | 36 | ||||
-rw-r--r-- | example.py | 33 | ||||
-rw-r--r-- | example2.py | 8 | ||||
-rw-r--r-- | example4.py | 1 | ||||
-rw-r--r-- | example5.py | 1 | ||||
-rw-r--r-- | pylint.conf | 28 | ||||
-rw-r--r-- | setup.py | 35 | ||||
-rw-r--r-- | test-plugins/aaa_hello_plugin.py | 2 | ||||
-rw-r--r-- | test-plugins/hello_plugin.py | 2 | ||||
-rw-r--r-- | test-plugins/oldhello_plugin.py | 3 | ||||
-rw-r--r-- | test-plugins/wrongversion_plugin.py | 2 |
24 files changed, 257 insertions, 209 deletions
@@ -26,3 +26,4 @@ check: python -m CoverageTestRunner --ignore-missing-from=without-tests rm .coverage pep8 cliapp + PYTHONPATH=. pylint --rcfile=pylint.conf cliapp *.py test-plugins/*.py @@ -12,6 +12,9 @@ Version 1.UNRELEASED `cliapp.ssh_runcmd` to allow the caller to easily change to a directory on the server. +* Support for `heapy` memory profile has been dropped. It's not + packaged for Debian, and is rarely available anywhere. + Version 1.20150305 ------------------ diff --git a/cliapp/__init__.py b/cliapp/__init__.py index 53cf077..05e8185 100644 --- a/cliapp/__init__.py +++ b/cliapp/__init__.py @@ -30,17 +30,17 @@ Homepage: http://liw.fi/cliapp/ __version__ = '1.20150305' -from fmt import TextFormat -from app import Application, AppException -from settings import (Settings, log_group_name, config_group_name, - perf_group_name, UnknownConfigVariable) -from runcmd import runcmd, runcmd_unchecked, shell_quote, ssh_runcmd +from .fmt import TextFormat +from .app import Application, AppException +from .settings import (Settings, log_group_name, config_group_name, + perf_group_name, UnknownConfigVariable) +from .runcmd import runcmd, runcmd_unchecked, shell_quote, ssh_runcmd # The plugin system -from hook import Hook, FilterHook -from hookmgr import HookManager -from plugin import Plugin -from pluginmgr import PluginManager +from .hook import Hook, FilterHook +from .hookmgr import HookManager +from .plugin import Plugin +from .pluginmgr import PluginManager __all__ = locals() diff --git a/cliapp/app.py b/cliapp/app.py index dc6b860..ddc273a 100644 --- a/cliapp/app.py +++ b/cliapp/app.py @@ -42,6 +42,7 @@ class AppException(Exception): ''' def __init__(self, msg): + Exception.__init__(self) self.msg = msg def __str__(self): @@ -124,16 +125,18 @@ class Application(object): def add_settings(self): '''Add application specific settings.''' - def run(self, args=None, stderr=sys.stderr, sysargv=sys.argv, + def run(self, args=None, stderr=sys.stderr, sysargv=None, log=logging.critical): '''Run the application.''' + sysargv = sys.argv if sysargv is None else sysargv + def run_it(): self._run(args=args, stderr=stderr, log=log) if self.settings.progname is None and sysargv: self.settings.progname = os.path.basename(sysargv[0]) - envname = '%s_PROFILE' % self._envname(self.settings.progname) + envname = '%s_PROFILE' % self.envname(self.settings.progname) profname = os.environ.get(envname, '') if profname: # pragma: no cover import cProfile @@ -141,7 +144,7 @@ class Application(object): else: run_it() - def _envname(self, progname): + def envname(self, progname): '''Create an environment variable name of the name of a program.''' basename = os.path.basename(progname) @@ -226,8 +229,9 @@ class Application(object): stderr.write(traceback.format_exc()) sys.exit(1) - logging.info('%s version %s ends normally' % - (self.settings.progname, self.settings.version)) + logging.info( + '%s version %s ends normally', + self.settings.progname, self.settings.version) def compute_setting_values(self, settings): '''Compute setting values after configs and options are parsed. @@ -302,9 +306,10 @@ class Application(object): description = fmt.format(self._format_subcommand_help(cmd)) text = '%s\n\n%s' % (usage, description) else: - usage = self._format_usage(all=show_all) + usage = self._format_usage(show_all=show_all) fmt = cliapp.TextFormat(width=width) - description = fmt.format(self._format_description(all=show_all)) + description = fmt.format( + self._format_description(show_all=show_all)) text = '%s\n\n%s' % (usage, description) text = self.settings.progname.join(text.split('%prog')) @@ -330,7 +335,7 @@ class Application(object): assert method.startswith('cmd_') return method[len('cmd_'):].replace('_', '-') - def _format_usage(self, all=False): + def _format_usage(self, show_all=False): '''Format usage, possibly also subcommands, if any.''' if self.subcommands: lines = [] @@ -349,12 +354,12 @@ class Application(object): args = self.cmd_synopsis.get(cmd, '') or '' return 'Usage: %%prog [options] %s %s' % (cmd, args) - def _format_description(self, all=False): + def _format_description(self, show_all=False): '''Format OptionParser description, with subcommand support.''' if self.subcommands: summaries = [] for cmd in sorted(self.subcommands.keys()): - if all or cmd not in self.hidden_subcommands: + if show_all or cmd not in self.hidden_subcommands: summaries.append(self._format_subcommand_summary(cmd)) cmd_desc = ''.join(summaries) return '%s\n%s' % (self._description or '', cmd_desc) @@ -458,24 +463,25 @@ class Application(object): return '%Y-%m-%d %H:%M:%S' def log_config(self): - logging.info('%s version %s starts' % - (self.settings.progname, self.settings.version)) - logging.debug('sys.argv: %s' % sys.argv) + logging.info( + '%s version %s starts', + self.settings.progname, self.settings.version) + logging.debug('sys.argv: %r', sys.argv) - logging.debug('current working directory: %s' % os.getcwd()) - logging.debug('uid: %d' % os.getuid()) - logging.debug('effective uid: %d' % os.geteuid()) - logging.debug('gid: %d' % os.getgid()) - logging.debug('effective gid: %d' % os.getegid()) + logging.debug('current working directory: %s', os.getcwd()) + logging.debug('uid: %s', os.getuid()) + logging.debug('effective uid: %s', os.geteuid()) + logging.debug('gid: %s', os.getgid()) + logging.debug('effective gid: %s', os.getegid()) logging.debug('environment variables:') for name in os.environ: - logging.debug('environment: %s=%s' % (name, os.environ[name])) + logging.debug('environment: %s=%s', name, os.environ[name]) cp = self.settings.as_cp() f = StringIO.StringIO() cp.write(f) - logging.debug('Config:\n%s' % f.getvalue()) - logging.debug('Python version: %s' % sys.version) + logging.debug('Config:\n%s', f.getvalue()) + logging.debug('Python version: %s', sys.version) def app_directory(self): '''Return the directory where the application class is defined. @@ -671,30 +677,26 @@ class Application(object): # Log wall clock and CPU times for self, children. utime, stime, cutime, cstime, elapsed_time = os.times() duration = elapsed_time - self._started - logging.debug('process duration: %s s' % duration) - logging.debug('CPU time, in process: %s s' % utime) - logging.debug('CPU time, in system: %s s' % stime) - logging.debug('CPU time, in children: %s s' % cutime) - logging.debug('CPU time, in system for children: %s s' % cstime) + logging.debug('process duration: %s s', duration) + logging.debug('CPU time, in process: %s s', utime) + logging.debug('CPU time, in system: %s s', stime) + logging.debug('CPU time, in children: %s s', cutime) + logging.debug('CPU time, in system for children: %s s', cstime) - logging.debug('dumping memory profiling data: %s' % msg) - logging.debug('VmRSS: %s KiB' % self._vmrss()) + logging.debug('dumping memory profiling data: %s', msg) + logging.debug('VmRSS: %s KiB', self._vmrss()) if kind == 'simple': return # These are fairly expensive operations, so we only log them # if we're doing expensive stuff anyway. - logging.debug('# objects: %d' % len(gc.get_objects())) - logging.debug('# garbage: %d' % len(gc.garbage)) - - if kind == 'heapy': - from guppy import hpy - h = hpy() - logging.debug('memory profile:\n%s' % h.heap()) - elif kind == 'meliae': - filename = 'obnam-%d.meliae' % self.memory_dump_counter - logging.debug('memory profile: see %s' % filename) + logging.debug('# objects: %d', len(gc.get_objects())) + logging.debug('# garbage: %d', len(gc.garbage)) + + if kind == 'meliae': + filename = 'obnam-%d.meliae', self.memory_dump_counter + logging.debug('memory profile: see %s', filename) from meliae import scanner scanner.dump_all_objects(filename) self.memory_dump_counter += 1 diff --git a/cliapp/app_tests.py b/cliapp/app_tests.py index a65aee2..fde0916 100644 --- a/cliapp/app_tests.py +++ b/cliapp/app_tests.py @@ -15,11 +15,8 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import optparse -import os import StringIO import sys -import tempfile import unittest import cliapp @@ -99,7 +96,7 @@ class ApplicationTests(unittest.TestCase): def test_run_processes_input_files(self): self.inputs = [] - self.app.process_input = lambda name: self.inputs.append(name) + self.app.process_input = self.inputs.append self.app.run(args=['foo', 'bar']) self.assertEqual(self.inputs, ['foo', 'bar']) @@ -126,12 +123,12 @@ class ApplicationTests(unittest.TestCase): self.assertEqual(self.called, ['foo', 'bar']) def test_makes_envname_correctly(self): - self.assertEqual(self.app._envname('foo'), 'FOO') - self.assertEqual(self.app._envname('foo.py'), 'FOO') - self.assertEqual(self.app._envname('foo bar'), 'FOO_BAR') - self.assertEqual(self.app._envname('foo-bar'), 'FOO_BAR') - self.assertEqual(self.app._envname('foo/bar'), 'BAR') - self.assertEqual(self.app._envname('foo_bar'), 'FOO_BAR') + self.assertEqual(self.app.envname('foo'), 'FOO') + self.assertEqual(self.app.envname('foo.py'), 'FOO') + self.assertEqual(self.app.envname('foo bar'), 'FOO_BAR') + self.assertEqual(self.app.envname('foo-bar'), 'FOO_BAR') + self.assertEqual(self.app.envname('foo/bar'), 'BAR') + self.assertEqual(self.app.envname('foo_bar'), 'FOO_BAR') def test_parses_options(self): self.app.settings.string(['foo'], 'foo help') @@ -142,31 +139,35 @@ class ApplicationTests(unittest.TestCase): def test_calls_setup(self): + context = {} + class App(cliapp.Application): def setup(self): - self.setup_called = True + context['setup-called'] = True def process_inputs(self, args): pass app = App() app.run(args=[]) - self.assertTrue(app.setup_called) + self.assertTrue(context['setup-called']) def test_calls_cleanup(self): + context = {} + class App(cliapp.Application): def cleanup(self): - self.cleanup_called = True + context['cleanup-called'] = True def process_inputs(self, args): pass app = App() app.run(args=[]) - self.assertTrue(app.cleanup_called) + self.assertTrue(context['cleanup-called']) def test_process_args_calls_process_inputs(self): self.called = False @@ -201,11 +202,11 @@ class ApplicationTests(unittest.TestCase): def test_open_input_opens_file(self): f = self.app.open_input('/dev/null') self.assert_(isinstance(f, file)) - self.assertEqual(f.mode, 'r') + self.assertEqual(getattr(f, 'mode'), 'r') def test_open_input_opens_file_in_binary_mode_when_requested(self): f = self.app.open_input('/dev/null', mode='rb') - self.assertEqual(f.mode, 'rb') + self.assertEqual(getattr(f, 'mode'), 'rb') def test_open_input_opens_stdin_if_dash_given(self): self.assertEqual(self.app.open_input('-'), sys.stdin) @@ -244,7 +245,7 @@ class ApplicationTests(unittest.TestCase): class Foo(cliapp.Application): - def open_input(self, name): + def open_input(self, name, mode=None): return StringIO.StringIO(''.join('%s%d\n' % (name, i) for i in range(2))) @@ -260,7 +261,7 @@ class ApplicationTests(unittest.TestCase): class Foo(cliapp.Application): - def open_input(self, name): + def open_input(self, name, mode=None): return StringIO.StringIO(''.join('%s%d\n' % (name, i) for i in range(2))) @@ -361,6 +362,6 @@ class ExtensibleSubcommandTests(unittest.TestCase): self.assertEqual(self.app.subcommands, {}) def test_adds_subcommand(self): - help = lambda args: None - self.app.add_subcommand('foo', help) - self.assertEqual(self.app.subcommands, {'foo': help}) + help_callback = lambda args: None + self.app.add_subcommand('foo', help_callback) + self.assertEqual(self.app.subcommands, {'foo': help_callback}) diff --git a/cliapp/hook_tests.py b/cliapp/hook_tests.py index 3e3de6e..b4779f5 100644 --- a/cliapp/hook_tests.py +++ b/cliapp/hook_tests.py @@ -24,6 +24,8 @@ class HookTests(unittest.TestCase): def setUp(self): self.hook = Hook() + self.args = None + self.kwargs = None def callback(self, *args, **kwargs): self.args = args @@ -57,6 +59,8 @@ class FilterHookTests(unittest.TestCase): def setUp(self): self.hook = FilterHook() + self.args = None + self.kwargs = None def callback(self, data, *args, **kwargs): self.args = args diff --git a/cliapp/plugin.py b/cliapp/plugin.py index cf964a3..8d9c167 100644 --- a/cliapp/plugin.py +++ b/cliapp/plugin.py @@ -26,11 +26,6 @@ other modules as well, such as unit tests, in the same locations.) ''' -import imp -import inspect -import os - - class Plugin(object): '''Base class for plugins. @@ -117,7 +112,7 @@ class Plugin(object): def enable(self): '''Enable the plugin.''' - raise NotImplemented() + raise NotImplementedError() def disable(self): # pragma: no cover '''Disable the plugin.''' diff --git a/cliapp/plugin_tests.py b/cliapp/plugin_tests.py index 951547e..9da69f7 100644 --- a/cliapp/plugin_tests.py +++ b/cliapp/plugin_tests.py @@ -20,6 +20,20 @@ import unittest from cliapp import Plugin +class DummyPlugin(Plugin): + + def __init__(self): + Plugin.__init__(self) + self.enable_called = False + self.disable_called = False + + def enable(self): + self.enable_called = True + + def disable(self): + self.disable_called = True + + class PluginTests(unittest.TestCase): def setUp(self): @@ -41,11 +55,11 @@ class PluginTests(unittest.TestCase): self.assertRaises(Exception, self.plugin.enable) def test_enable_wrapper_calls_enable(self): - self.plugin.enable = lambda: setattr(self, 'enabled', True) - self.plugin.enable_wrapper() - self.assert_(self.enabled, True) + plugin = DummyPlugin() + plugin.enable_wrapper() + self.assertTrue(plugin.enable_called) def test_disable_wrapper_calls_disable(self): - self.plugin.disable = lambda: setattr(self, 'disabled', True) - self.plugin.disable_wrapper() - self.assert_(self.disabled, True) + plugin = DummyPlugin() + plugin.disable_wrapper() + self.assertTrue(plugin.disable_called) diff --git a/cliapp/pluginmgr.py b/cliapp/pluginmgr.py index 96c726e..ac856b8 100644 --- a/cliapp/pluginmgr.py +++ b/cliapp/pluginmgr.py @@ -126,8 +126,8 @@ class PluginManager(object): def load_plugin_file(self, pathname): '''Return plugin classes in a plugin file.''' - name, ext = os.path.splitext(os.path.basename(pathname)) - f = file(pathname, 'r') + name, _ = os.path.splitext(os.path.basename(pathname)) + f = open(pathname, 'r') module = imp.load_module(name, f, pathname, ('.py', 'r', imp.PY_SOURCE)) f.close() @@ -161,13 +161,13 @@ class PluginManager(object): return [int(s) for s in version.split('.')] - def enable_plugins(self, plugins=None): + def enable_plugins(self, plugins=None): # pragma: no cover '''Enable all or selected plugins.''' for plugin in plugins or self.plugins: plugin.enable_wrapper() - def disable_plugins(self, plugins=None): + def disable_plugins(self, plugins=None): # pragma: no cover '''Disable all or selected plugins.''' for plugin in plugins or self.plugins: diff --git a/cliapp/pluginmgr_tests.py b/cliapp/pluginmgr_tests.py index ccca868..e2a79f1 100644 --- a/cliapp/pluginmgr_tests.py +++ b/cliapp/pluginmgr_tests.py @@ -80,20 +80,6 @@ class PluginManagerTests(unittest.TestCase): def test_raises_keyerror_for_unknown_plugin(self): self.assertRaises(KeyError, self.pm.__getitem__, 'Hithere') - def test_enable_plugins_enables_all_plugins(self): - enabled = set() - for plugin in self.pm.plugins: - plugin.enable = lambda: enabled.add(plugin) - self.pm.enable_plugins() - self.assertEqual(enabled, set(self.pm.plugins)) - - def test_disable_plugins_disables_all_plugins(self): - disabled = set() - for plugin in self.pm.plugins: - plugin.disable = lambda: disabled.add(plugin) - self.pm.disable_plugins() - self.assertEqual(disabled, set(self.pm.plugins)) - class PluginManagerCompatibleApplicationVersionTests(unittest.TestCase): diff --git a/cliapp/runcmd.py b/cliapp/runcmd.py index 6ae2524..b59da70 100644 --- a/cliapp/runcmd.py +++ b/cliapp/runcmd.py @@ -51,8 +51,8 @@ def runcmd(argv, *args, **kwargs): opts[name] = kwargs[name] del kwargs[name] - exit, out, err = runcmd_unchecked(argv, *args, **kwargs) - if exit != 0: + exit_code, out, err = runcmd_unchecked(argv, *args, **kwargs) + if exit_code != 0: msg = 'Command failed: %s\n%s' % (' '.join(argv), err) if opts['ignore_fail']: if opts['log_error']: @@ -75,7 +75,7 @@ def runcmd_unchecked(argv, *argvs, **kwargs): ''' argvs = [argv] + list(argvs) - logging.debug('run external command: %s' % repr(argvs)) + logging.debug('run external command: %r', argvs) def pop_kwarg(name, default): if name in kwargs: @@ -206,10 +206,9 @@ def _run_pipeline(procs, feed_stdin, pipe_stdin, pipe_stdout, pipe_stderr, if rlist or wlist: try: - r, w, x = select.select(rlist, wlist, []) - except select.error, e: # pragma: no cover - err, msg = e.args - if err == errno.EINTR: + r, w, _ = select.select(rlist, wlist, []) + except select.error as e: # pragma: no cover + if e.args[0] == errno.EINTR: break raise else: diff --git a/cliapp/runcmd_tests.py b/cliapp/runcmd_tests.py index ce7ef8a..878a1b9 100644 --- a/cliapp/runcmd_tests.py +++ b/cliapp/runcmd_tests.py @@ -16,11 +16,8 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import optparse import os -import StringIO import subprocess -import sys import tempfile import unittest @@ -44,8 +41,8 @@ class RuncmdTests(unittest.TestCase): 'hello world\n') def test_runcmd_returns_stderr_of_command(self): - exit, out, err = cliapp.runcmd_unchecked(['ls', 'notexist']) - self.assertNotEqual(exit, 0) + exit_code, out, err = cliapp.runcmd_unchecked(['ls', 'notexist']) + self.assertNotEqual(exit_code, 0) self.assertEqual(out, '') self.assertNotEqual(err, '') @@ -87,7 +84,7 @@ class RuncmdTests(unittest.TestCase): (0, '4\n', '')) def test_runcmd_redirects_stdin_from_file(self): - fd, filename = tempfile.mkstemp() + fd, _ = tempfile.mkstemp() os.write(fd, 'foobar') os.lseek(fd, 0, os.SEEK_SET) self.assertEqual(cliapp.runcmd_unchecked(['cat'], stdin=fd), @@ -96,31 +93,32 @@ class RuncmdTests(unittest.TestCase): def test_runcmd_redirects_stdout_to_file(self): fd, filename = tempfile.mkstemp() - exit, out, err = cliapp.runcmd_unchecked(['echo', 'foo'], stdout=fd) + exit_code, _, _ = cliapp.runcmd_unchecked( + ['echo', 'foo'], stdout=fd) os.close(fd) with open(filename) as f: data = f.read() - self.assertEqual(exit, 0) + self.assertEqual(exit_code, 0) self.assertEqual(data, 'foo\n') def test_runcmd_redirects_stderr_to_file(self): fd, filename = tempfile.mkstemp() - exit, out, err = cliapp.runcmd_unchecked(['ls', 'notexist'], stderr=fd) + exit_code, _, _ = cliapp.runcmd_unchecked( + ['ls', 'notexist'], stderr=fd) os.close(fd) with open(filename) as f: data = f.read() - self.assertNotEqual(exit, 0) + self.assertNotEqual(exit_code, 0) self.assertNotEqual(data, '') def test_runcmd_unchecked_handles_stdout_err_redirected_to_same_file(self): fd, filename = tempfile.mkstemp() - exit, out, err = cliapp.runcmd_unchecked(['sleep', '2'], - stdout=fd, - stderr=subprocess.STDOUT) + exit_code, _, _ = cliapp.runcmd_unchecked( + ['sleep', '2'], stdout=fd, stderr=subprocess.STDOUT) os.close(fd) with open(filename) as f: data = f.read() - self.assertEqual(exit, 0) + self.assertEqual(exit_code, 0) self.assertEqual(data, '') def test_runcmd_calls_stdout_callback_when_msg_on_stdout(self): @@ -134,8 +132,8 @@ class RuncmdTests(unittest.TestCase): return 'foo' test_input = 'hello fox' - exit, out, err = cliapp.runcmd_unchecked(['echo', '-n', test_input], - stdout_callback=logger) + _, out, _ = cliapp.runcmd_unchecked( + ['echo', '-n', test_input], stdout_callback=logger) self.assertEqual(out, 'foo') self.assertEqual(msgs, [test_input]) @@ -150,8 +148,8 @@ class RuncmdTests(unittest.TestCase): # mangled. return None - exit, out, err = cliapp.runcmd_unchecked(['ls', 'nosuchthing'], - stderr_callback=logger) + _, _, err = cliapp.runcmd_unchecked( + ['ls', 'nosuchthing'], stderr_callback=logger) # The callback may be called several times, and we have no # control over that: output from the subprocess may arrive in diff --git a/cliapp/settings.py b/cliapp/settings.py index 80fb461..62586fd 100644 --- a/cliapp/settings.py +++ b/cliapp/settings.py @@ -39,8 +39,8 @@ default_group_names = [ class UnknownConfigVariable(cliapp.AppException): def __init__(self, filename, name): - self.msg = ( - '%s: Unknown configuration variable %s' % (filename, name)) + msg = '%s: Unknown configuration variable %s' % (filename, name) + cliapp.AppException.__init__(self, msg) def __str__(self): # pragma: no cover return self.msg @@ -53,11 +53,11 @@ class Setting(object): nargs = 1 choices = None - def __init__(self, names, default, help, metavar=None, group=None, + def __init__(self, names, default, help_text, metavar=None, group=None, hidden=False): self.names = names self.set_value(default) - self.help = help + self.help = help_text self.metavar = metavar or self.default_metavar() self.group = group self.hidden = hidden @@ -99,10 +99,11 @@ class StringListSetting(Setting): action = 'append' - def __init__(self, names, default, help, metavar=None, group=None, + def __init__(self, names, default, help_text, metavar=None, group=None, hidden=False): Setting.__init__( - self, names, [], help, metavar=metavar, group=group, hidden=hidden) + self, names, [], help_text, metavar=metavar, group=group, + hidden=hidden) self.default = default self.using_default_value = True @@ -133,10 +134,10 @@ class ChoiceSetting(Setting): type = 'choice' - def __init__(self, names, choices, help, metavar=None, group=None, + def __init__(self, names, choices, help_text, metavar=None, group=None, hidden=False): Setting.__init__( - self, names, choices[0], help, metavar=metavar, group=group, + self, names, choices[0], help_text, metavar=metavar, group=group, hidden=hidden) self.choices = choices @@ -312,9 +313,9 @@ class Settings(object): metavar='MODE', default='0600', group=log_group_name) self.choice(['dump-memory-profile'], - ['simple', 'none', 'meliae', 'heapy'], + ['simple', 'none', 'meliae'], 'make memory profiling dumps using METHOD, which is one ' - 'of: none, simple, meliae, or heapy ' + 'of: none, simple, or meliae ' '(default: %default)', metavar='METHOD', group=perf_group_name) @@ -331,11 +332,11 @@ class Settings(object): for name in setting.names: self._settingses[name] = setting - def string(self, names, help, default='', **kwargs): + def string(self, names, help_text, default='', **kwargs): '''Add a setting with a string value.''' - self._add_setting(StringSetting(names, default, help, **kwargs)) + self._add_setting(StringSetting(names, default, help_text, **kwargs)) - def string_list(self, names, help, default=None, **kwargs): + def string_list(self, names, help_text, default=None, **kwargs): '''Add a setting which have multiple string values. An example would be an option that can be given multiple times @@ -343,10 +344,10 @@ class Settings(object): ''' - self._add_setting(StringListSetting(names, default or [], help, + self._add_setting(StringListSetting(names, default or [], help_text, **kwargs)) - def choice(self, names, possibilities, help, **kwargs): + def choice(self, names, possibilities, help_text, **kwargs): '''Add a setting which chooses from list of acceptable values. An example would be an option to set debugging level to be @@ -356,24 +357,25 @@ class Settings(object): ''' - self._add_setting(ChoiceSetting(names, possibilities, help, **kwargs)) + self._add_setting( + ChoiceSetting(names, possibilities, help_text, **kwargs)) - def boolean(self, names, help, default=False, **kwargs): + def boolean(self, names, help_text, default=False, **kwargs): '''Add a setting with a boolean value.''' - self._add_setting(BooleanSetting(names, default, help, **kwargs)) + self._add_setting(BooleanSetting(names, default, help_text, **kwargs)) - def bytesize(self, names, help, default=0, **kwargs): + def bytesize(self, names, help_text, default=0, **kwargs): '''Add a setting with a size in bytes. The user can use suffixes for kilo/mega/giga/tera/kibi/mibi/gibi/tibi. ''' - self._add_setting(ByteSizeSetting(names, default, help, **kwargs)) + self._add_setting(ByteSizeSetting(names, default, help_text, **kwargs)) - def integer(self, names, help, default=0, **kwargs): + def integer(self, names, help_text, default=0, **kwargs): '''Add an integer setting.''' - self._add_setting(IntegerSetting(names, default, help, **kwargs)) + self._add_setting(IntegerSetting(names, default, help_text, **kwargs)) def __getitem__(self, name): return self._settingses[name].value @@ -424,7 +426,7 @@ class Settings(object): return name def build_parser(self, configs_only=False, arg_synopsis=None, - cmd_synopsis=None, deferred_last=[], all_options=False): + cmd_synopsis=None, deferred_last=None, all_options=False): '''Build OptionParser for parsing command line.''' # Call a callback function unless we're in configs_only mode. @@ -660,7 +662,7 @@ class Settings(object): if suppress_errors: p.error = lambda msg: sys.exit(1) - options, args = p.parse_args(args) + _, args = p.parse_args(args) if compute_setting_values: # pragma: no cover compute_setting_values(self) for callback in deferred_last: # pragma: no cover @@ -668,7 +670,7 @@ class Settings(object): return args @property - def _default_config_files(self): + def default_config_files(self): '''Return list of default config files to read. The names of the files are dependent on the name of the program, @@ -681,14 +683,14 @@ class Settings(object): configs = [] configs.append('/etc/%s.conf' % self.progname) - configs += self._listconfs('/etc/%s' % self.progname) + configs += self.listconfs('/etc/%s' % self.progname) configs.append(os.path.expanduser('~/.%s.conf' % self.progname)) - configs += self._listconfs( + configs += self.listconfs( os.path.expanduser('~/.config/%s' % self.progname)) return configs - def _listconfs(self, dirname, listdir=os.listdir): + def listconfs(self, dirname, listdir=os.listdir): '''Return list of pathnames to config files in dirname. Config files are expectd to have names ending in '.conf'. @@ -709,7 +711,7 @@ class Settings(object): def _get_config_files(self): if self._config_files is None: - self._config_files = self._default_config_files + self._config_files = self.default_config_files return self._config_files def _set_config_files(self, config_files): @@ -725,7 +727,7 @@ class Settings(object): s.parse_value(raw_string) return s - def load_configs(self, open=open): + def load_configs(self, open_file=open): '''Load all config files in self.config_files. Silently ignore files that do not exist. @@ -737,7 +739,7 @@ class Settings(object): for pathname in self.config_files: try: - f = open(pathname) + f = open_file(pathname) except IOError: pass else: @@ -753,7 +755,7 @@ class Settings(object): # Remember the ConfigParser for use in as_cp later on. self._cp = cp - def _generate_manpage(self, o, os, value, p): # pragma: no cover + def _generate_manpage(self, o, dummy, value, p): # pragma: no cover template = open(value).read() generator = ManpageGenerator(template, p, self._arg_synopsis, self._cmd_synopsis) diff --git a/cliapp/settings_tests.py b/cliapp/settings_tests.py index b5b9eaa..1d6fa84 100644 --- a/cliapp/settings_tests.py +++ b/cliapp/settings_tests.py @@ -15,9 +15,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import optparse import StringIO -import sys import unittest import cliapp @@ -171,14 +169,14 @@ class SettingsTests(unittest.TestCase): def fake_open(filename): return StringIO.StringIO('[config]\nfoo = yes\n') self.settings.boolean(['foo'], 'foo help') - self.settings.load_configs(open=fake_open) + self.settings.load_configs(open_file=fake_open) self.assertEqual(self.settings['foo'], True) def test_sets_boolean_to_false_from_config_file(self): def fake_open(filename): return StringIO.StringIO('[config]\nfoo = False\n') self.settings.boolean(['foo'], 'foo help') - self.settings.load_configs(open=fake_open) + self.settings.load_configs(open_file=fake_open) self.assertEqual(self.settings['foo'], False) def test_adds_bytesize_setting(self): @@ -232,33 +230,33 @@ class SettingsTests(unittest.TestCase): self.assertEqual(self.settings['foo'], 123) def test_has_list_of_default_config_files(self): - defaults = self.settings._default_config_files + defaults = self.settings.default_config_files self.assert_(isinstance(defaults, list)) self.assert_(len(defaults) > 0) def test_listconfs_returns_empty_list_for_nonexistent_directory(self): - self.assertEqual(self.settings._listconfs('notexist'), []) + self.assertEqual(self.settings.listconfs('notexist'), []) def test_listconfs_lists_config_files_only(self): def mock_listdir(dirname): return ['foo.conf', 'foo.notconf'] - names = self.settings._listconfs('.', listdir=mock_listdir) + names = self.settings.listconfs('.', listdir=mock_listdir) self.assertEqual(names, ['./foo.conf']) def test_listconfs_sorts_names_in_C_locale(self): def mock_listdir(dirname): return ['foo.conf', 'bar.conf'] - names = self.settings._listconfs('.', listdir=mock_listdir) + names = self.settings.listconfs('.', listdir=mock_listdir) self.assertEqual(names, ['./bar.conf', './foo.conf']) def test_has_config_files_attribute(self): self.assertEqual(self.settings.config_files, - self.settings._default_config_files) + self.settings.default_config_files) def test_has_config_files_list_can_be_changed(self): self.settings.config_files += ['./foo'] self.assertEqual(self.settings.config_files, - self.settings._default_config_files + ['./foo']) + self.settings.default_config_files + ['./foo']) def test_loads_config_files(self): @@ -270,7 +268,7 @@ foo = yeehaa self.settings.string(['foo'], 'foo help') self.settings.config_files = ['whatever.conf'] - self.settings.load_configs(open=mock_open) + self.settings.load_configs(open_file=mock_open) self.assertEqual(self.settings['foo'], 'yeehaa') def test_loads_string_list_from_config_files(self): @@ -285,7 +283,7 @@ bar = ping, pong self.settings.string_list(['foo'], 'foo help') self.settings.string_list(['bar'], 'bar help') self.settings.config_files = ['whatever.conf'] - self.settings.load_configs(open=mock_open) + self.settings.load_configs(open_file=mock_open) self.assertEqual(self.settings['foo'], ['yeehaa']) self.assertEqual(self.settings['bar'], ['ping', 'pong']) @@ -299,7 +297,7 @@ bar = ping, pong self.settings.string(['foo'], 'foo help', default='foo') self.settings.string_list(['bar'], 'bar help', default=['bar']) self.settings.config_files = ['whatever.conf'] - self.settings.load_configs(open=mock_open) + self.settings.load_configs(open_file=mock_open) self.assertEqual(self.settings['foo'], 'foo') self.assertEqual(self.settings['bar'], ['bar']) @@ -315,7 +313,7 @@ bar = ping, pong self.settings.string(['foo'], 'foo help', default='foo') self.settings.string_list(['bar'], 'bar help', default=['bar']) self.settings.config_files = ['whatever.conf'] - self.settings.load_configs(open=mock_open) + self.settings.load_configs(open_file=mock_open) self.assertEqual(self.settings['foo'], 'yeehaa') self.assertEqual(self.settings['bar'], ['ping', 'pong']) @@ -331,7 +329,7 @@ bar = ping, pong self.settings.string(['foo'], 'foo help', default='foo') self.settings.string_list(['bar'], 'bar help', default=['bar']) self.settings.config_files = ['whatever.conf'] - self.settings.load_configs(open=mock_open) + self.settings.load_configs(open_file=mock_open) self.settings.parse_args(['--foo=red', '--bar=blue', '--bar=white']) self.assertEqual(self.settings['foo'], 'red') self.assertEqual(self.settings['bar'], ['blue', 'white']) @@ -347,19 +345,19 @@ unknown = variable self.assertRaises( cliapp.UnknownConfigVariable, self.settings.load_configs, - open=mock_open) + open_file=mock_open) def test_load_configs_ignore_errors_opening_a_file(self): def mock_open(filename, mode=None): raise IOError() - self.assertEqual(self.settings.load_configs(open=mock_open), None) + self.assertEqual(self.settings.load_configs(open_file=mock_open), None) def test_adds_config_file_with_dash_dash_config(self): self.settings.parse_args(['--config=foo.conf']) self.assertEqual(self.settings.config_files, - self.settings._default_config_files + ['foo.conf']) + self.settings.default_config_files + ['foo.conf']) def test_ignores_default_configs(self): self.settings.parse_args(['--no-default-configs']) @@ -444,7 +442,7 @@ bar = dodo self.settings.string(['foo'], 'foo help', default='foo') self.settings.config_files = ['whatever.conf'] - self.settings.load_configs(open=mock_open) + self.settings.load_configs(open_file=mock_open) cp = self.settings.as_cp() self.assertEqual(sorted(cp.sections()), ['config', 'other']) @@ -31,16 +31,25 @@ class ExampleApp(cliapp.Application): '''A little fgrep-like tool.''' def add_settings(self): - self.settings.string_list(['pattern', 'e'], - 'search for regular expression PATTERN', - metavar='REGEXP') - - self.settings.boolean(['dummy'], 'this setting is ignored', - group='Test Group') - - self.settings.string(['yoyo'], 'yoyo', group=cliapp.config_group_name) - - self.settings.string(['nono'], 'nono', default=None) + self.settings.string_list( + ['pattern', 'e'], + 'search for regular expression PATTERN', + metavar='REGEXP') + + self.settings.boolean( + ['dummy'], + 'this setting is ignored', + group='Test Group') + + self.settings.string( + ['yoyo'], + 'yoyo', + group=cliapp.config_group_name) + + self.settings.string( + ['nono'], + 'nono', + default=None) # We override process_inputs to be able to do something after the last # input line. @@ -50,12 +59,12 @@ class ExampleApp(cliapp.Application): self.output.write('There were %s matches.\n' % self.matches) def process_input_line(self, name, line): - logging.debug('processing %s:%s' % (name, self.lineno)) + logging.debug('processing %s:%s', name, self.lineno) for pattern in self.settings['pattern']: if pattern in line: self.output.write('%s:%s: %s' % (name, self.lineno, line)) self.matches += 1 - logging.debug('Match: %s line %d' % (name, self.lineno)) + logging.debug('Match: %s line %d', name, self.lineno) app = ExampleApp(version='0.1.2') diff --git a/example2.py b/example2.py index 1f061b9..972e880 100644 --- a/example2.py +++ b/example2.py @@ -23,7 +23,6 @@ Greet or insult people. import cliapp -import logging class ExampleApp(cliapp.Application): @@ -54,14 +53,17 @@ class ExampleApp(cliapp.Application): self.output.write('you suck, %s\n' % arg) -app = ExampleApp(version='0.1.2', description=''' +app = ExampleApp( + version='0.1.2', + description=''' Greet the user. Or possibly insult them. User's choice. ''', -epilog=''' + epilog=''' This is the epilog. I hope you like it. ''') + app.run() diff --git a/example4.py b/example4.py index f500bbd..c6ba78f 100644 --- a/example4.py +++ b/example4.py @@ -16,7 +16,6 @@ import cliapp -import logging class ExampleApp(cliapp.Application): diff --git a/example5.py b/example5.py index 2e83bae..c6ab7b2 100644 --- a/example5.py +++ b/example5.py @@ -16,7 +16,6 @@ import cliapp -import logging class LoggerApp(cliapp.Application): diff --git a/pylint.conf b/pylint.conf new file mode 100644 index 0000000..b36b2aa --- /dev/null +++ b/pylint.conf @@ -0,0 +1,28 @@ +[MASTER] +persistent=no + +[MESSAGES CONTROL] +disable= + attribute-defined-outside-init, + bad-builtin, + blacklisted-name, + cyclic-import, + invalid-name, + missing-docstring, + no-self-use, + protected-access, + star-args, + too-few-public-methods, + too-many-arguments, + too-many-branches, + too-many-instance-attributes, + too-many-locals, + too-many-public-methods, + too-many-statements, + unused-argument + +[REPORTS] +reports=no + +[SIMILARITIES] +min-similarity-lines=999 @@ -21,19 +21,20 @@ import glob import cliapp -setup(name='cliapp', - version=cliapp.__version__, - author='Lars Wirzenius', - author_email='liw@liw.fi', - url='http://liw.fi/cliapp/', - description='framework for Unix command line programs', - long_description='''\ -cliapp makes it easier to write typical Unix command line programs, -by taking care of the common tasks they need to do, such as -parsing the command line, reading configuration files, setting -up logging, iterating over lines of input files, and so on. -''', - classifiers=[ +setup( + name='cliapp', + version=cliapp.__version__, + author='Lars Wirzenius', + author_email='liw@liw.fi', + url='http://liw.fi/cliapp/', + description='framework for Unix command line programs', + long_description='''\ + cliapp makes it easier to write typical Unix command line programs, + by taking care of the common tasks they need to do, such as + parsing the command line, reading configuration files, setting + up logging, iterating over lines of input files, and so on. + ''', + classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: Developers', @@ -44,7 +45,7 @@ up logging, iterating over lines of input files, and so on. 'Topic :: Software Development :: User Interfaces', 'Topic :: Text Processing :: Filters', 'Topic :: Utilities', - ], - packages=['cliapp'], - data_files=[('share/man/man5', glob.glob('*.5'))], - ) + ], + packages=['cliapp'], + data_files=[('share/man/man5', glob.glob('*.5'))], +) diff --git a/test-plugins/aaa_hello_plugin.py b/test-plugins/aaa_hello_plugin.py index b8f3253..9af3f83 100644 --- a/test-plugins/aaa_hello_plugin.py +++ b/test-plugins/aaa_hello_plugin.py @@ -6,3 +6,5 @@ class Hello(cliapp.Plugin): self.foo = foo self.bar = bar + def enable(self): + pass diff --git a/test-plugins/hello_plugin.py b/test-plugins/hello_plugin.py index a102502..c47adb7 100644 --- a/test-plugins/hello_plugin.py +++ b/test-plugins/hello_plugin.py @@ -13,3 +13,5 @@ class Hello(cliapp.Plugin): def setup(self): self.setup_called = True + def enable(self): + pass diff --git a/test-plugins/oldhello_plugin.py b/test-plugins/oldhello_plugin.py index e401ff7..9af3f83 100644 --- a/test-plugins/oldhello_plugin.py +++ b/test-plugins/oldhello_plugin.py @@ -6,4 +6,5 @@ class Hello(cliapp.Plugin): self.foo = foo self.bar = bar - + def enable(self): + pass diff --git a/test-plugins/wrongversion_plugin.py b/test-plugins/wrongversion_plugin.py index 9f54908..caf6307 100644 --- a/test-plugins/wrongversion_plugin.py +++ b/test-plugins/wrongversion_plugin.py @@ -10,3 +10,5 @@ class WrongVersion(cliapp.Plugin): def __init__(self, *args, **kwargs): pass + def enable(self): + pass |