Skip to content

Commit

Permalink
Version bump correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucretiel committed Aug 10, 2017
2 parents fe4a839 + 1fdb6c9 commit 88064c2
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 7 deletions.
6 changes: 4 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,16 @@ If the default value is a file object, such as ``sys.stdout``, then autocommand
Descriptions and docstrings
~~~~~~~~~~~~~~~~~~~~~~~~~~~

The ``autocommand`` decorator accepts ``description`` and ``epilog`` kwargs, corresponding to the `description <https://docs.python.org/3/library/argparse.html#description>`_ and `epilog <https://docs.python.org/3/library/argparse.html#epilog>`_ of the ``ArgumentParser``. If no description is given, but the decorated function has a docstring, then it is taken as the ``description`` for the ``ArgumentParser``
The ``autocommand`` decorator accepts ``description`` and ``epilog`` kwargs, corresponding to the `description <https://docs.python.org/3/library/argparse.html#description>`_ and `epilog <https://docs.python.org/3/library/argparse.html#epilog>`_ of the ``ArgumentParser``. If no description is given, but the decorated function has a docstring, then it is taken as the ``description`` for the ``ArgumentParser``. You can also provide both the description and epilog in the docstring by splitting it into two sections with 4 or more - characters.

.. code:: python
@autocommand(__name__, epilog='Some extra documentation in the epilog')
@autocommand(__name__)
def copy(infile=sys.stdin, outfile=sys.stdout):
'''
Copy an the contents of a file (or stdin) to another file (or stdout)
----------
Some extra documentation in the epilog
'''
with smart_open(infile) as istr:
with smart_open(outfile, 'w') as ostr:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def getfile(filename):

setup(
name='autocommand',
version='2.1.5',
version='2.2.0',
packages=[
'autocommand'
],
Expand Down
39 changes: 37 additions & 2 deletions src/autocommand/autoparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# along with autocommand. If not, see <http://www.gnu.org/licenses/>.

import sys
from re import compile as compile_regex
from inspect import signature, getdoc, Parameter
from argparse import ArgumentParser
from contextlib import contextmanager
Expand All @@ -41,6 +42,18 @@ class KWArgError(AutocommandError):
'''kwarg Error: autocommand can't handle a **kwargs parameter'''


class DocstringError(AutocommandError):
'''Docstring error'''


class TooManySplitsError(DocstringError):
'''
The docstring had too many ---- section splits. Currently we only support
using up to a single split, to split the docstring into description and
epilog parts.
'''


def _get_type_description(annotation):
'''
Given an annotation, return the (type, description) for the parameter.
Expand Down Expand Up @@ -196,6 +209,26 @@ def make_parser(func_sig, description, epilog, add_nos):
return parser


_DOCSTRING_SPLIT = compile_regex(r'\n\s*-{4,}\s*\n')


def parse_docstring(docstring):
'''
Given a docstring, parse it into a description and epilog part
'''
if docstring is None:
return '', ''

parts = _DOCSTRING_SPLIT.split(docstring)

if len(parts) == 1:
return docstring, ''
elif len(parts) == 2:
return parts[0], parts[1]
else:
raise TooManySplitsError()


def autoparse(
func=None, *,
description=None,
Expand Down Expand Up @@ -245,11 +278,13 @@ def autoparse(

func_sig = signature(func)

docstr_description, docstr_epilog = parse_docstring(getdoc(func))

if parser is None:
parser = make_parser(
func_sig,
description or getdoc(func),
epilog,
description or docstr_description,
epilog or docstr_epilog,
add_nos)

@wraps(func)
Expand Down
12 changes: 11 additions & 1 deletion test/test_autoparse/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def check_parse_impl(function, *args, add_nos=False, **kwargs):

@pytest.fixture
def check_help_text(capsys):
def check_help_text_impl(func, *texts):
def check_help_text_impl(func, *texts, reject=()):
'''
This helper checks that some set of text is written to stdout or stderr
after the called function raises a SystemExit. It is used to test that
Expand All @@ -54,14 +54,24 @@ def check_help_text_impl(func, *texts):
*texts: A set of strings to test for. All of the provided strings will
be checked for in the captured stdout/stderr using a standard
substring search.
reject: A string or set of strings to check do NOT exist anywhere in
the output. Currently used as a cludge to test the docstring split
behavior.
'''
with pytest.raises(SystemExit):
func()

out, err = capsys.readouterr()

# TODO: be wary of argparse's text formatting
# TODO: test that the text appears in the order given
for text in texts:
assert text in out or text in err

if isinstance(reject, str):
reject = [reject]

for text in reject:
assert text not in out and text not in err

return check_help_text_impl
37 changes: 36 additions & 1 deletion test/test_autoparse/test_invocation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from unittest.mock import patch
from argparse import ArgumentParser
import pytest
from autocommand.autoparse import autoparse
from autocommand.autoparse import autoparse, TooManySplitsError


def test_basic_invocation():
Expand Down Expand Up @@ -56,6 +56,41 @@ def func(arg):
'This is a docstring description')


def test_docstring_description_epilog(check_help_text):
@autoparse
def func(arg):
'''
This is the description
-------
This is the epilog
'''
pass

created_parser = func.parser
assert 'This is the description' in created_parser.description
assert 'This is the epilog' in created_parser.epilog

check_help_text(
lambda: func(['-h']),
'This is the description',
'This is the epilog',
reject='-------')


def test_bad_docstring():
with pytest.raises(TooManySplitsError):
@autoparse
def func(arg):
'''
Part 1
-------
Part 2
-------
Part 3
'''
pass


def test_custom_parser():
parser = ArgumentParser()

Expand Down

0 comments on commit 88064c2

Please sign in to comment.