Skip to content

Instantly share code, notes, and snippets.

@scottt
Created September 1, 2013 22:52
Show Gist options
  • Save scottt/6407845 to your computer and use it in GitHub Desktop.
Save scottt/6407845 to your computer and use it in GitHub Desktop.
diff between the forked _cmdln.py in applib and cmdln git master
--- /home/scottt/work/python-applib/applib/applib/_cmdln.py 2013-09-02 06:31:58.187831448 +0800
+++ lib/cmdln.py 2013-09-02 06:46:06.076666182 +0800
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+# Copyright (c) 2012 Trent Mick
# Copyright (c) 2002-2009 ActiveState Software Inc.
# License: MIT (see LICENSE.txt for license details)
# Author: Trent Mick
@@ -30,33 +31,23 @@
retval = shell.main()
sys.exit(retval)
-See the README.txt or <http://code.google.com/p/cmdln/> for more
+See the README.txt or <http://trentm.com/projects/cmdln/> for more
details.
"""
-__version_info__ = (1, 2, 0)
+__version_info__ = (1, 3, 0)
__version__ = '.'.join(map(str, __version_info__))
import os
-from os import path
import sys
import re
-import types
import cmd
import optparse
from pprint import pprint
-try:
- import ConfigParser
-except ImportError:
- import configparser as ConfigParser # python3
+import sys
import datetime
-if sys.hexversion > 0x03000000:
- ClassType = type
-else:
- ClassType = types.ClassType
-
#---- globals
@@ -69,7 +60,7 @@
# failed because of incorrect number of arguments (see
# Python/getargs.c).
_INCORRECT_NUM_ARGS_RE = re.compile(
- r"(takes [\w ]+ )(\d+)[\w ]*( arguments? \()(\d+)( given\))")
+ r"(takes [\w ]+ )(\d+)( arguments? \()(\d+)( given\))")
@@ -92,7 +83,7 @@
def alias(*aliases):
"""Decorator to add aliases for Cmdln.do_* command handlers.
-
+
Example:
class MyShell(cmdln.Cmdln):
@cmdln.alias("!", "sh")
@@ -139,7 +130,7 @@
helpindent = '' # string with which to indent help output
- def __init__(self, completekey='tab',
+ def __init__(self, completekey='tab',
stdin=None, stdout=None, stderr=None):
"""Cmdln(completekey='tab', stdin=None, stdout=None, stderr=None)
@@ -147,15 +138,16 @@
completion key; it defaults to the Tab key. If completekey is
not None and the readline module is available, command completion
is done automatically.
-
+
The optional arguments 'stdin', 'stdout' and 'stderr' specify
alternate input, output and error output file objects; if not
specified, sys.* are used.
-
+
If 'stdout' but not 'stderr' is specified, stdout is used for
error output. This is to provide least surprise for users used
to only the 'stdin' and 'stdout' options with cmd.Cmd.
"""
+ import sys
if self.name is None:
self.name = os.path.basename(sys.argv[0])
if self.prompt is None:
@@ -180,60 +172,32 @@
self.completekey = completekey
self.cmdlooping = False
- def get_option_defaults(self, cmdname):
- """Return default values for command options
-
- For all options registered for the given command (`cmdname`), return
- the default values as a dictionary (option name as keys, default value
- as values)
-
- If `cmdname` is None, return default for top-level options
- """
- return {}
-
def get_optparser(self):
"""Hook for subclasses to set the option parser for the
top-level command/shell.
- NOTE: you may not override this method anymore; cmdln.option decorator
- can now be used on the class itself to create toplevel options.
-
- This option parser is retrieved and used by `.main()' to handle
- top-level options.
+ This option parser is used retrieved and used by `.main()' to
+ handle top-level options.
The default implements a single '-h|--help' option. Sub-classes
can return None to have no options at the top-level. Typically
an instance of CmdlnOptionParser should be returned.
"""
- return self._create_toplevel_optparser()
-
- def _create_toplevel_optparser(self):
- version = (self.version is not None
+ version = (self.version is not None
and "%s %s" % (self._name_str, self.version)
or None)
- parser = CmdlnOptionParser(self, version=version)
-
- # if ``useconfig`` is used, add the -c option to specify extra config
- # file
- # if hasattr(self, 'defaultsconfig'):
- # parser.add_option('-c', '--configfile',
- # dest='configfile',
- # help="specify the config file location",
- # default=None)
-
- # add toplevel options
- if hasattr(self, 'toplevel_optparser_options'):
- for args, kwargs in self.toplevel_optparser_options:
- parser.add_option(*args, **kwargs)
-
- return parser
+ return CmdlnOptionParser(self, version=version)
def postoptparse(self):
"""Hook method executed just after `.main()' parses top-level
options.
When called `self.options' holds the results of the option parse.
+
+ If this returns non-zero/non-None, then command processing is stopped
+ and this retval is returned from `main()`.
"""
+ pass
def main(self, argv=None, loop=LOOP_NEVER):
"""A possible mainline handler for a script, like so:
@@ -242,14 +206,14 @@
class MyCmd(cmdln.Cmdln):
name = "mycmd"
...
-
+
if __name__ == "__main__":
MyCmd().main()
By default this will use sys.argv to issue a single command to
'MyCmd', then exit. The 'loop' argument can be use to control
interactive shell behaviour.
-
+
Arguments:
"argv" (optional, default sys.argv) is the command to run.
It must be a sequence, where the first element is the
@@ -264,34 +228,29 @@
otherwise, start loop
"""
if argv is None:
+ import sys
argv = sys.argv
else:
argv = argv[:] # don't modify caller's list
- try:
- self.optparser = self.get_optparser()
- if self.optparser: # i.e. optparser=None means don't process for opts
- try:
- self.options, args = self.optparser.parse_args(argv[1:])
- except StopOptionProcessing:
- return 0
- else:
- # Set default options *after* parsing command line options
- # This is an requirement for CmdlnWithConfigParser which
- # relies on the -c option which is only parsed in the above
- # `try' block
- self.optparser.set_defaults(**self.get_option_defaults(None))
- self.options, args = self.optparser.parse_args(argv[1:])
- else:
- self.options, args = None, argv[1:]
- self.postoptparse()
- except CmdlnUserError:
- _, ex, _ = sys.exc_info()
- msg = "%s: %s\nTry '%s help' for info.\n"\
- % (self.name, ex, self.name)
- self.stderr.write(self._str(msg))
- self.stderr.flush()
- return 1
+ self.optparser = self.get_optparser()
+ if self.optparser: # i.e. optparser=None means don't process for opts
+ try:
+ self.options, args = self.optparser.parse_args(argv[1:])
+ except CmdlnUserError, ex:
+ msg = "%s: %s\nTry '%s help' for info.\n"\
+ % (self.name, ex, self.name)
+ self.stderr.write(self._str(msg))
+ self.stderr.flush()
+ return 1
+ except StopOptionProcessing, ex:
+ return 0
+ else:
+ self.options, args = None, argv[1:]
+
+ retval = self.postoptparse()
+ if retval:
+ return retval
if loop == LOOP_ALWAYS:
if args:
@@ -310,7 +269,7 @@
def cmd(self, argv):
"""Run one command and exit.
-
+
"argv" is the arglist for the command to run. argv[0] is the
command to run. If argv is an empty list then the
'emptyline' handler is run.
@@ -338,6 +297,7 @@
#XXX What is the proper encoding to use here? 'utf-8' seems
# to work better than "getdefaultencoding" (usually
# 'ascii'), on OS X at least.
+ #import sys
#return s.encode(sys.getdefaultencoding(), "replace")
return s.encode("utf-8", "replace")
@@ -345,7 +305,7 @@
"""Repeatedly issue a prompt, accept input, parse into an argv, and
dispatch (via .precmd(), .onecmd() and .postcmd()), passing them
the argv. In other words, start a shell.
-
+
"intro" (optional) is a introductory message to print when
starting the command loop. This overrides the class
"intro" attribute, if any.
@@ -357,7 +317,10 @@
import readline
self.old_completer = readline.get_completer()
readline.set_completer(self.complete)
- readline.parse_and_bind(self.completekey+": complete")
+ if sys.platform == "darwin":
+ readline.parse_and_bind("bind ^I rl_complete")
+ else:
+ readline.parse_and_bind(self.completekey+": complete")
except ImportError:
pass
try:
@@ -415,7 +378,7 @@
interpreted, but after the input prompt is generated and issued.
"argv" is the cmd to run.
-
+
Returns an argv to run (i.e. this method can modify the command
to run).
"""
@@ -423,7 +386,7 @@
def postcmd(self, argv):
"""Hook method executed just after a command dispatch is finished.
-
+
"argv" is the command that was run.
"""
pass
@@ -438,6 +401,7 @@
opposed to programmer error in the design of the script using
cmdln.py).
"""
+ import sys
type, exc, traceback = sys.exc_info()
if isinstance(exc, CmdlnUserError):
msg = "%s %s: %s\nTry '%s help %s' for info.\n"\
@@ -467,7 +431,7 @@
"""Hook called to handle a command for which there is no handler.
"argv" is the command and arguments to run.
-
+
The default implementation writes an error message to stderr
and returns an error exit status.
@@ -503,7 +467,7 @@
"cmd" is the command name on which help was requested.
"known" is a boolean indicating if this command is known
(i.e. if there is a handler for it).
-
+
Returns a return code.
"""
if known:
@@ -629,7 +593,7 @@
i.e. using @cmdln.option decorators or manually setting the
'optparser' attribute on the 'do_*' method.)
- Returns the processed help.
+ Returns the processed help.
"""
preprocessors = {
"${name}": self._help_preprocess_name,
@@ -664,7 +628,7 @@
block = self.optparser.format_option_help() + '\n'
else:
block = ""
-
+
help = help.replace(indent+marker+suffix, block, 1)
return help
@@ -682,7 +646,8 @@
for attr in self.get_names():
if attr.startswith("do_"):
cmdnames[attr[3:]] = True
- cmdnames = list(sorted(cmdnames.keys()))
+ cmdnames = cmdnames.keys()
+ cmdnames.sort()
linedata = []
for cmdname in cmdnames:
if aliases.get(cmdname):
@@ -700,7 +665,7 @@
doc = handler.__doc__
else:
doc = helpfunc()
-
+
# Strip "${cmd_name}: " from the start of a command's doc. Best
# practice dictates that command help strings begin with this, but
# it isn't at all wanted for the command list.
@@ -710,9 +675,9 @@
# to_strip, cmdname)
doc = doc[len(to_strip):].lstrip()
linedata.append( (cmdstr, doc) )
-
+
return linedata
-
+
def _help_preprocess_command_list(self, help, cmdname=None):
marker = "${command_list}"
indent, indent_width = _get_indent(marker, help)
@@ -777,7 +742,7 @@
handler = self._get_cmd_handler(cmdname)
if not handler:
raise CmdlnError("cannot preprocess '%s' into help string: "
- "could not find command handler for %r"
+ "could not find command handler for %r"
% (marker, cmdname))
s = cmdname
if hasattr(handler, "aliases"):
@@ -793,20 +758,20 @@
handler = self._get_cmd_handler(cmdname)
if not handler:
raise CmdlnError("cannot preprocess '%s' into help string: "
- "could not find command handler for %r"
+ "could not find command handler for %r"
% (marker, cmdname))
indent, indent_width = _get_indent(marker, help)
suffix = _get_trailing_whitespace(marker, help)
# Extract the introspection bits we need.
- func = handler.__func__
- if func.__defaults__:
- func_defaults = list(func.__defaults__)
+ func = handler.im_func
+ if func.func_defaults:
+ func_defaults = list(func.func_defaults)
else:
func_defaults = []
- co_argcount = func.__code__.co_argcount
- co_varnames = func.__code__.co_varnames
- co_flags = func.__code__.co_flags
+ co_argcount = func.func_code.co_argcount
+ co_varnames = func.func_code.co_varnames
+ co_flags = func.func_code.co_flags
CO_FLAGS_ARGS = 4
CO_FLAGS_KWARGS = 8
@@ -829,9 +794,9 @@
# keyword arguments from the command line. Could
# *perhaps* consider: arg=value arg2=value2 ...
warnings.warn("argument '**%s' on '%s.%s' command "
- "handler will never get values"
+ "handler will never get values"
% (name, self.__class__.__name__,
- func.__name__))
+ func.func_name))
if co_flags & CO_FLAGS_ARGS:
name = argnames.pop(-1)
tail = "[%s...]" % name.upper()
@@ -861,7 +826,7 @@
handler = self._get_cmd_handler(cmdname)
if not handler:
raise CmdlnError("cannot preprocess '%s' into help string: "
- "could not find command handler for %r"
+ "could not find command handler for %r"
% (marker, cmdname))
indent, indent_width = _get_indent(marker, help)
suffix = _get_trailing_whitespace(marker, help)
@@ -960,7 +925,7 @@
StopIteration. This is raised by _OptionParserEx's default "help"
and "version" option actions and can be raised by custom option
callbacks too.
-
+
Hence the typical CmdlnOptionParser (a subclass of _OptionParserEx)
usage is:
@@ -1052,44 +1017,27 @@
def error(self, msg):
raise CmdlnUserError(msg)
-
+
def option(*args, **kwargs):
"""Decorator to add an option to the optparser argument of a Cmdln
- subcommand
+ subcommand.
- To add a toplevel option, apply the decorator on the class itself. (see
- p4.py for an example)
-
Example:
- @cmdln.option("-E", dest="environment_path")
class MyShell(cmdln.Cmdln):
@cmdln.option("-f", "--force", help="force removal")
def do_remove(self, subcmd, opts, *args):
#...
"""
- def decorate_sub_command(method):
- """create and add sub-command options"""
- if not hasattr(method, "optparser"):
- method.optparser = SubCmdOptionParser()
- method.optparser.add_option(*args, **kwargs)
- return method
- def decorate_class(klass):
- """store toplevel options"""
- assert _forgiving_issubclass(klass, Cmdln)
- _inherit_attr(klass, "toplevel_optparser_options", [], cp=lambda l: l[:])
- klass.toplevel_optparser_options.append( (args, kwargs) )
- return klass
-
#XXX Is there a possible optimization for many options to not have a
# large stack depth here?
- def decorate(obj):
- if _forgiving_issubclass(obj, Cmdln):
- return decorate_class(obj)
- else:
- return decorate_sub_command(obj)
+ def decorate(f):
+ if not hasattr(f, "optparser"):
+ f.optparser = SubCmdOptionParser()
+ f.optparser.add_option(*args, **kwargs)
+ return f
return decorate
-
+
class Cmdln(RawCmdln):
"""An improved (on cmd.Cmd) framework for building multi-subcommand
scripts (think "svn" & "cvs") and simple shells (think "pdb" and
@@ -1119,7 +1067,6 @@
integration. See this class' _dispatch_cmd() docstring and general
cmdln document for more information.
"""
-
def _dispatch_cmd(self, handler, argv):
"""Introspect sub-command handler signature to determine how to
dispatch the command. The raw handler provided by the base
@@ -1157,20 +1104,15 @@
and an appropriate error message will be raised/printed if the
command is called with a different number of args.
"""
- co_argcount = handler.__func__.__code__.co_argcount
+ co_argcount = handler.im_func.func_code.co_argcount
if co_argcount == 2: # handler ::= do_foo(self, argv)
return handler(argv)
elif co_argcount >= 3: # handler ::= do_foo(self, subcmd, opts, ...)
try:
optparser = handler.optparser
except AttributeError:
- optparser = handler.__func__.optparser = SubCmdOptionParser()
+ optparser = handler.im_func.optparser = SubCmdOptionParser()
assert isinstance(optparser, SubCmdOptionParser)
-
- # apply subcommand options' defaults from config files, if any.
- subcmd = handler.__name__.split('do_', 1)[1]
- optparser.set_defaults(**self.get_option_defaults(subcmd))
-
optparser.set_cmdln_info(self, argv[0])
try:
opts, args = optparser.parse_args(argv[1:])
@@ -1181,15 +1123,14 @@
try:
return handler(argv[0], opts, *args)
- except TypeError:
- _, ex, _ = sys.exc_info()
+ except TypeError, ex:
# Some TypeError's are user errors:
# do_foo() takes at least 4 arguments (3 given)
# do_foo() takes at most 5 arguments (6 given)
# do_foo() takes exactly 5 arguments (6 given)
- # do_foo() takes exactly 5 positional arguments (6 given)
# Raise CmdlnUserError for these with a suitably
# massaged error message.
+ import sys
tb = sys.exc_info()[2] # the traceback object
if tb.tb_next is not None:
# If the traceback is more than one level deep, then the
@@ -1221,15 +1162,15 @@
def man_sections_from_cmdln(inst, summary=None, description=None, author=None):
"""Return man page sections appropriate for the given Cmdln instance.
Join these sections for man page content.
-
+
The man page sections generated are:
NAME
SYNOPSIS
DESCRIPTION (if `description` is given)
OPTIONS
COMMANDS
- HELP TOPICS (if any)
-
+ HELP TOPICS (if any)
+
@param inst {Cmdln} Instance of Cmdln subclass for which to generate
man page content.
@param summary {str} A one-liner summary of the command.
@@ -1246,12 +1187,12 @@
data = {
"name": inst.name,
"ucname": inst.name.upper(),
- "date": datetime.date.today().strftime("%b %Y"),
+ "date": datetime.date.today().strftime("%b %Y"),
"cmdln_version": __version__,
"version_str": inst.version and " %s" % inst.version or "",
"summary_str": summary and r" \- %s" % summary or "",
}
-
+
sections = []
sections.append('.\\" Automatically generated by cmdln %(cmdln_version)s\n'
'.TH %(ucname)s "1" "%(date)s" "%(name)s%(version_str)s" "User Commands"\n'
@@ -1292,7 +1233,7 @@
doc = doc.rstrip() + "\n" # trim down trailing space
section += '.PP\n.SS %s\n%s\n' % (cmdstr, doc)
sections.append(section)
-
+
help_names = inst._get_help_names()
if help_names:
section = ".SH HELP TOPICS\n"
@@ -1311,36 +1252,9 @@
#---- internal support functions
-def _inherit_attr(klass, attr, default, cp):
- """Inherit the attribute from the base class
-
- Copy `attr` from base class (otherwise use `default`). Copying is done using
- the passed `cp` function.
-
- The motivation behind writing this function is to allow inheritance among
- Cmdln classes where base classes set 'common' options using the
- `@cmdln.option` decorator. To ensure this, we must not write to the base
- class's options when handling the derived class.
- """
- if attr not in klass.__dict__:
- if hasattr(klass, attr):
- value = cp(getattr(klass, attr))
- else:
- value = default
- setattr(klass, attr, value)
-
-def _forgiving_issubclass(derived_class, base_class):
- """Forgiving version of ``issubclass``
-
- Does not throw any exception when arguments are not of class type
- """
- return (type(derived_class) is ClassType and \
- type(base_class) is ClassType and \
- issubclass(derived_class, base_class))
-
def _format_linedata(linedata, indent, indent_width):
"""Format specific linedata into a pleasant layout.
-
+
"linedata" is a list of 2-tuples of the form:
(<item-display-string>, <item-docstring>)
"indent" is a string to use for one level of indentation
@@ -1374,7 +1288,7 @@
def _summarize_doc(doc, length=60):
r"""Parse out a short one line summary from the given doclines.
-
+
"doc" is the doc string to summarize.
"length" is the max length for the summary
@@ -1406,19 +1320,19 @@
summary = ' '.join(summlines)
if len(summary) > length:
- summary = summary[:length-3] + "..."
+ summary = summary[:length-3] + "..."
return summary
def line2argv(line):
r"""Parse the given line into an argument vector.
-
+
"line" is the line of input to parse.
This may get niggly when dealing with quoting and escaping. The
current state of this parsing may not be completely thorough/correct
in this respect.
-
+
>>> from cmdln import line2argv
>>> line2argv("foo")
['foo']
@@ -1430,7 +1344,7 @@
['foo', 'bar']
Quote handling:
-
+
>>> line2argv("'foo bar'")
['foo bar']
>>> line2argv('"foo bar"')
@@ -1441,7 +1355,7 @@
['foo bar', 'spam']
>>> line2argv("'foo 'bar spam")
['foo bar', 'spam']
-
+
>>> line2argv('some\tsimple\ttests')
['some', 'simple', 'tests']
>>> line2argv('a "more complex" test')
@@ -1533,9 +1447,9 @@
def argv2line(argv):
r"""Put together the given argument vector into a command line.
-
+
"argv" is the argument vector to process.
-
+
>>> from cmdln import argv2line
>>> argv2line(['foo'])
'foo'
@@ -1568,20 +1482,20 @@
# Recipe: dedent (0.1) in /Users/trentm/tm/recipes/cookbook
def _dedentlines(lines, tabsize=8, skip_first_line=False):
"""_dedentlines(lines, tabsize=8, skip_first_line=False) -> dedented lines
-
+
"lines" is a list of lines to dedent.
"tabsize" is the tab width to use for indent width calculations.
"skip_first_line" is a boolean indicating if the first line should
be skipped for calculating the indent width and for dedenting.
This is sometimes useful for docstrings and similar.
-
+
Same as dedent() except operates on a sequence of lines. Note: the
lines list is modified **in-place**.
"""
DEBUG = False
- if DEBUG:
- print("dedent: dedent(..., tabsize=%d, skip_first_line=%r)"\
- % (tabsize, skip_first_line))
+ if DEBUG:
+ print "dedent: dedent(..., tabsize=%d, skip_first_line=%r)"\
+ % (tabsize, skip_first_line)
indents = []
margin = None
for i, line in enumerate(lines):
@@ -1598,12 +1512,12 @@
break
else:
continue # skip all-whitespace lines
- if DEBUG: print("dedent: indent=%d: %r" % (indent, line))
+ if DEBUG: print "dedent: indent=%d: %r" % (indent, line)
if margin is None:
margin = indent
else:
margin = min(margin, indent)
- if DEBUG: print("dedent: margin=%r" % margin)
+ if DEBUG: print "dedent: margin=%r" % margin
if margin is not None and margin > 0:
for i, line in enumerate(lines):
@@ -1615,7 +1529,7 @@
elif ch == '\t':
removed += tabsize - (removed % tabsize)
elif ch in '\r\n':
- if DEBUG: print("dedent: %r: EOL -> strip up to EOL" % line)
+ if DEBUG: print "dedent: %r: EOL -> strip up to EOL" % line
lines[i] = lines[i][j:]
break
else:
@@ -1623,8 +1537,8 @@
"line %r while removing %d-space margin"
% (ch, line, margin))
if DEBUG:
- print("dedent: %r: %r -> removed %d/%d"\
- % (line, ch, removed, margin))
+ print "dedent: %r: %r -> removed %d/%d"\
+ % (line, ch, removed, margin)
if removed == margin:
lines[i] = lines[i][j+1:]
break
@@ -1641,7 +1555,7 @@
"skip_first_line" is a boolean indicating if the first line should
be skipped for calculating the indent width and for dedenting.
This is sometimes useful for docstrings and similar.
-
+
textwrap.dedent(s), but don't expand tabs to spaces
"""
lines = text.splitlines(1)
@@ -1737,8 +1651,7 @@
try:
script = _module_from_path(script_path)
- except ImportError:
- _, ex, _ = sys.exc_info()
+ except ImportError, ex:
_log("error importing `%s': %s" % (script_path, ex))
return []
shell = getattr(script, class_name)()
@@ -1787,57 +1700,4 @@
return []
for cpln in _get_bash_cplns(*sys.argv[1:]):
- print(cpln)
-
-
-
-## -- contrib --
-
-@option("-c", "--configfile", dest="configfile", default=None,
- metavar='FILENAME',
- help='Configuration file to read options from')
-class CmdlnWithConfigParser(Cmdln):
- """Cmdln with configparser support
-
- Add a new option -c --configfile for reading config file; and set
- default values for both toplevel and command-specific options.
-
- See examples/cfgexample.py
- """
-
- class NoConfigFile(Exception): pass
-
- def __init__(self, default_configfile=None, *args, **kwargs):
- Cmdln.__init__(self, *args, **kwargs)
- self._cfgparser = None
- self._default_configfile = default_configfile
-
- def get_optparser(self):
- parser = Cmdln.get_optparser(self)
- parser.set_default('configfile', self._default_configfile)
- return parser
-
- def _load_config(self):
- if not self._cfgparser:
- if self.options.configfile:
- self._cfgparser = ConfigParser.SafeConfigParser()
- if not path.exists(self.options.configfile):
- raise CmdlnUserError(
- 'config file "%s" does not exist' % \
- self.options.configfile)
- self._cfgparser.read(self.options.configfile)
- else:
- raise self.NoConfigFile
-
- def get_option_defaults(self, cmd):
- try:
- self._load_config()
- except self.NoConfigFile:
- return {}
- else:
- section = cmd or 'cmdln'
- try:
- return dict(self._cfgparser.items(section))
- except ConfigParser.NoSectionError:
- return {}
-
+ print cpln
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment