summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2015-04-18 11:18:54 +0300
committerLars Wirzenius <liw@liw.fi>2015-04-18 15:41:49 +0300
commita09e1064f58673591093df7f3b709a775c06b866 (patch)
tree173e13e3a3293e4a1871bb1fa2bb1ad3cd07d938
parentd3b79bed48895150fb7c33f577716a900fda3833 (diff)
downloadcliapp-a09e1064f58673591093df7f3b709a775c06b866.tar.gz
Add running of pylint in 'make check' and fix things
-rw-r--r--Makefile1
-rw-r--r--NEWS3
-rw-r--r--cliapp/__init__.py18
-rw-r--r--cliapp/app.py78
-rw-r--r--cliapp/app_tests.py43
-rw-r--r--cliapp/hook_tests.py4
-rw-r--r--cliapp/plugin.py7
-rw-r--r--cliapp/plugin_tests.py26
-rw-r--r--cliapp/pluginmgr.py8
-rw-r--r--cliapp/pluginmgr_tests.py14
-rw-r--r--cliapp/runcmd.py13
-rw-r--r--cliapp/runcmd_tests.py34
-rw-r--r--cliapp/settings.py66
-rw-r--r--cliapp/settings_tests.py36
-rw-r--r--example.py33
-rw-r--r--example2.py8
-rw-r--r--example4.py1
-rw-r--r--example5.py1
-rw-r--r--pylint.conf28
-rw-r--r--setup.py35
-rw-r--r--test-plugins/aaa_hello_plugin.py2
-rw-r--r--test-plugins/hello_plugin.py2
-rw-r--r--test-plugins/oldhello_plugin.py3
-rw-r--r--test-plugins/wrongversion_plugin.py2
24 files changed, 257 insertions, 209 deletions
diff --git a/Makefile b/Makefile
index a64f92c..edfdc49 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/NEWS b/NEWS
index 7de119c..abda82e 100644
--- a/NEWS
+++ b/NEWS
@@ -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'])
diff --git a/example.py b/example.py
index 14a56db..fe282be 100644
--- a/example.py
+++ b/example.py
@@ -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
diff --git a/setup.py b/setup.py
index 2ed1543..6a7024d 100644
--- a/setup.py
+++ b/setup.py
@@ -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