diff --git a/core/doc/pm3argparse.rst b/core/doc/pm3argparse.rst index cc823ef3c50d26bd88f6ba715551f4ce3e95dc98..359b6755d179c0fec98ddf1c0139207d14d77575 100644 --- a/core/doc/pm3argparse.rst +++ b/core/doc/pm3argparse.rst @@ -26,8 +26,9 @@ Argument Parser .. automethod:: __init__ .. |descattr| replace:: :attr:`description` -.. |argpinit| replace:: :meth:`ArgumentParser.__init__` +.. |argpinit| replace:: :meth:`argparse.ArgumentParser.__init__` .. |progattr| replace:: :attr:`prog` +.. |sysargv| replace:: :attr:`sys.argv` .. LocalWords: currentmodule argparse ArgumentParser autoclass automethod .. LocalWords: init descattr attr argpinit meth progattr prog diff --git a/core/pymod/core/pm3argparse.py b/core/pymod/core/pm3argparse.py index ca0753bd8ec30b7183e28ce74e9726e092e64279..670c445395c51002ecd4397b4201dfde0aa46e48 100644 --- a/core/pymod/core/pm3argparse.py +++ b/core/pymod/core/pm3argparse.py @@ -16,6 +16,10 @@ class PM3ArgumentParser(argparse.ArgumentParser): this, everything you can do with a 'real' :class:`~argparse.ArgumentParser` instance is possible here. + A note on exit codes: if :meth:`~pm3argparse.PM3ArgumentParser.Parse` is + called on unrecognised arguments, the script exits with a code 2 by + :class:`argparse.ArgumentParser.parse_args()`. + Attributes beyond :class:`argparse.ArgumentParser`: .. attribute:: action @@ -52,6 +56,22 @@ class PM3ArgumentParser(argparse.ArgumentParser): self.action = action + def Parse(self, args=None, namespace=None): + """ + Parse an argument string. + + :param args: The argument string. As default |sysargv|_ is used. + :type args: :class:`list` + + :param namespace: The same as for + :meth:`argparse.ArgumentParser.parse_args`. + + :returns: If :attr:`namespace` is not given, + :class:`argparse.Namespace`. + """ + opts = self.parse_args(args=args, namespace=namespace) + return opts + # LocalWords: param attr prog argparse ArgumentParser bool sys os init str -# LocalWords: progattr descattr argpinit argv formatter meth -# LocalWords: ArgumentDefaultsHelpFormatter +# LocalWords: progattr descattr argpinit argv formatter meth args namespace +# LocalWords: ArgumentDefaultsHelpFormatter sysargv diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt index 51736e2fbf75cba2ffd657b3a9a9e25439261fe3..df293726d86ea5631933df29e35fa79efd92dc8a 100644 --- a/core/tests/CMakeLists.txt +++ b/core/tests/CMakeLists.txt @@ -1,6 +1,7 @@ set(CORE_UNIT_TESTS test_setcompoundschemlib.py test_argcheck.py + test_pm3argparse.py ) set(CORE_TEST_DATA diff --git a/core/tests/test_pm3argparse.py b/core/tests/test_pm3argparse.py new file mode 100644 index 0000000000000000000000000000000000000000..9b4dff068f6e013ff4a0548b206d41b55420c613 --- /dev/null +++ b/core/tests/test_pm3argparse.py @@ -0,0 +1,70 @@ +import unittest, sys +from promod3.core import pm3argparse + +#argparse writes to stdout/ stderr so we need to fetch it +class _FetchStd(object): + def __init__(self): + self.buffer = list() + + def write(self, line): + self.buffer.append(line.strip()) + + def flush(self): + pass + +class PM3ArgParseTests(unittest.TestCase): + def setUp(self): + self.original_stderr = sys.stderr + self.original_stdout = sys.stdout + sys.stderr = _FetchStd() + sys.stdout = _FetchStd() + + def tearDown(self): + sys.stderr = self.original_stderr + sys.stdout = self.original_stdout + + def testUnrecognisedArguments(self): + parser = pm3argparse.PM3ArgumentParser(action=False) + with self.assertRaises(SystemExit) as ec: + parser.Parse(['-x']) + self.assertEqual(ec.exception.code, 2) + self.assertEqual(sys.stderr.buffer, ['usage: test_pm3argparse.py [-h]', + 'test_pm3argparse.py: error: '+ + 'unrecognized arguments: -x']) + + def testActionSwitch(self): + parser = pm3argparse.PM3ArgumentParser(action=False) + with self.assertRaises(SystemExit) as ec: + parser.Parse(['--help']) + self.assertEqual(ec.exception.code, 0) + self.assertEqual(sys.stdout.buffer, ['usage: test_pm3argparse.py '+ + '[-h]\n\noptional arguments:\n '+ + '-h, --help show this help '+ + 'message and exit']) + + sys.stdout.buffer = list() + parser = pm3argparse.PM3ArgumentParser(action=True) + with self.assertRaises(SystemExit) as ec: + parser.Parse(['--help']) + self.assertEqual(ec.exception.code, 0) + self.assertEqual(sys.stdout.buffer, ['usage: t_pm3argparse.py '+ + '[-h]\n\noptional arguments:\n '+ + '-h, --help show this help '+ + 'message and exit']) + + + def testDescription(self): + parser = pm3argparse.PM3ArgumentParser(action=False, + description='This is a test.') + with self.assertRaises(SystemExit) as ec: + parser.Parse(['--help']) + self.assertEqual(ec.exception.code, 0) + self.assertEqual(sys.stdout.buffer, ['usage: test_pm3argparse.py '+ + '[-h]\n\nThis is a '+ + 'test.\n\noptional arguments:\n '+ + '-h, --help show this help '+ + 'message and exit']) + +if __name__ == "__main__": + from ost import testutils + testutils.RunTests() diff --git a/doc/conf.py.in b/doc/conf.py.in index a122ac3fb3d51c088f755c5a2c71fada8a4b91ca..737213b7e04c103165ffba73745b56b428dd9e97 100644 --- a/doc/conf.py.in +++ b/doc/conf.py.in @@ -289,6 +289,7 @@ rst_epilog = """ .. _descattr: @PYTHON_DOC_URL@/library/argparse.html#description .. _progattr: @PYTHON_DOC_URL@/library/argparse.html#prog .. _argpinit: @PYTHON_DOC_URL@/library/argparse.html#argparse.ArgumentParser +.. _sysargv: @PYTHON_DOC_URL@/library/sys.html#sys.argv .. |pep8| replace:: PEP 8 .. _pep8: https://www.python.org/dev/peps/pep-0008/ """ % project @@ -308,7 +309,7 @@ class DocTestLogger(ost.LogSink): def LogMessage(self, message, severity): # there is a reason why we do not put 'severity' to any use in the message - # written to stdou. If we modify the message handed in by a doctest, + # written to stdout. If we modify the message handed in by a doctest, # verifying it would mean the creator of the doctest would have to check # that very code here to get the test right. sys.stdout.write('%s' % message) diff --git a/extras/pre_commit/pm3_csc/filecheck/python.py b/extras/pre_commit/pm3_csc/filecheck/python.py index ae769056accbcab4323d2db2afc38560e1b5e799..a000d534aeedc7e8ae300bc50632900516333202 100644 --- a/extras/pre_commit/pm3_csc/filecheck/python.py +++ b/extras/pre_commit/pm3_csc/filecheck/python.py @@ -14,7 +14,7 @@ class Python(base.FileCheck): def __init__(self, filepath): base.FileCheck.__init__(self, filepath) - def CheckPylint(self, outline): + def CheckPylint(self, outline, rcfile): """ Translate Pylint messages to something more meaningful. @@ -66,9 +66,7 @@ class Python(base.FileCheck): "--help-msg=%s'. Once this is resolved, " % msg[1] + "you could extend '%s' with more " % __file__ + "meaningful information. You can run pylint on "+ - "this file by 'pylint "+ - "--rcfile=%s " % os.path.join(this_path, - 'pylintrc')+ + "this file by 'pylint --rcfile=%s " % rcfile + "-r n %s'." % self.absfilepath, 16) subst_dict = {'line' : msg[0], 'file' : self.filepath} return msg_dict[msg[1]] % subst_dict @@ -105,11 +103,11 @@ class Python(base.FileCheck): for line in sout.splitlines(): if line.startswith('*************'): continue - msg_stack.append(self.CheckPylint(line)) + msg_stack.append(self.CheckPylint(line, pylintrc)) for line in serr.splitlines(): if line.startswith('*************'): continue - msg_stack.append(self.CheckPylint(line)) + msg_stack.append(self.CheckPylint(line, pylintrc)) if len(msg_stack): pm3_csc.FailMsg(os.linesep + "Found issues:" + os.linesep + os.linesep.join(msg_stack), 17)