Hello community, here is the log from the commit of package python-sybil for openSUSE:Factory checked in at 2019-05-06 13:28:19 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-sybil (Old) and /work/SRC/openSUSE:Factory/.python-sybil.new.5148 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-sybil" Mon May 6 13:28:19 2019 rev:7 rq:700972 version:1.2.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-sybil/python-sybil.changes 2019-03-29 20:42:34.754678409 +0100 +++ /work/SRC/openSUSE:Factory/.python-sybil.new.5148/python-sybil.changes 2019-05-06 13:28:21.781399836 +0200 @@ -1,0 +2,15 @@ +Fri May 3 11:20:28 UTC 2019 - pgaj...@suse.com + +- version update to 1.2.0 + * Only compile code in :ref:`codeblocks <codeblock-parser>` at evaluation time, + giving :ref:`skip <skip-parser>` a chance to skip code blocks that won't + compile on a particular version of Python. + * Add warning about the limitations of + :attr:`~sybil.parsers.doctest.FIX_BYTE_UNICODE_REPR`. + * Support explicit filenames to include and patterns to exclude + when instantiating a :class:`~sybil.Sybil`. + * Add the :ref:`skip <skip-parser>` parser. +- deleted patches + - sybil-pytest4.patch (upstreamed) + +------------------------------------------------------------------- Old: ---- sybil-1.0.9.tar.gz sybil-pytest4.patch New: ---- sybil-1.2.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-sybil.spec ++++++ --- /var/tmp/diff_new_pack.l9HO7S/_old 2019-05-06 13:28:23.209402945 +0200 +++ /var/tmp/diff_new_pack.l9HO7S/_new 2019-05-06 13:28:23.249403032 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-sybil # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,14 +18,13 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-sybil -Version: 1.0.9 +Version: 1.2.0 Release: 0 Summary: Automated testing of examples in documentation License: MIT Group: Development/Languages/Python Url: https://github.com/cjw296/sybil Source: https://files.pythonhosted.org/packages/source/s/sybil/sybil-%{version}.tar.gz -Patch0: sybil-pytest4.patch BuildRequires: %{python_module nose} BuildRequires: %{python_module pytest >= 3.5.0} BuildRequires: %{python_module setuptools-git} @@ -46,7 +45,6 @@ %prep %setup -q -n sybil-%{version} -%patch0 -p1 sed -i '/build=/d;/coveralls/d' setup.py %build ++++++ sybil-1.0.9.tar.gz -> sybil-1.2.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/.carthorse.yml new/sybil-1.2.0/.carthorse.yml --- old/sybil-1.0.9/.carthorse.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sybil-1.2.0/.carthorse.yml 2019-04-28 08:57:09.000000000 +0200 @@ -0,0 +1,10 @@ +carthorse: + version-from: setup.py + tag-format: "{version}" + when: + - version-not-tagged + actions: + - run: "sudo pip install -e .[build]" + - run: "sudo python setup.py sdist bdist_wheel" + - run: "twine upload -u chrisw -p $PYPI_PASS dist/*" + - create-tag diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/.circleci/config.yml new/sybil-1.2.0/.circleci/config.yml --- old/sybil-1.0.9/.circleci/config.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sybil-1.2.0/.circleci/config.yml 2019-04-28 08:57:09.000000000 +0200 @@ -0,0 +1,41 @@ +version: 2.1 + +orbs: + python: cjw296/python-ci@1.2 + +common: &common + jobs: + + - python/pip-run-tests: + name: python27 + image: circleci/python:2.7 + - python/pip-run-tests: + name: python37 + image: circleci/python:3.7 + + - python/coverage: + name: coverage + requires: + - python27 + - python37 + + - python/release: + name: release + config: .carthorse.yml + requires: + - coverage + filters: + branches: + only: master + +workflows: + push: + <<: *common + periodic: + <<: *common + triggers: + - schedule: + cron: "0 0 * * 2" + filters: + branches: + only: master diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/.readthedocs.yml new/sybil-1.2.0/.readthedocs.yml --- old/sybil-1.0.9/.readthedocs.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sybil-1.2.0/.readthedocs.yml 2019-04-28 08:57:09.000000000 +0200 @@ -0,0 +1,8 @@ +version: 2 +python: + version: 3.7 + install: + - method: pip + path: . + extra_requirements: + - build diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/.travis.yml new/sybil-1.2.0/.travis.yml --- old/sybil-1.0.9/.travis.yml 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/.travis.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,30 +0,0 @@ -language: python - -# for container-y goodness: -sudo: false - -python: - - "2.7" - - "3.6" - -install: - - "pip install --upgrade pip setuptools" - - "pip install -Ue .[test,build]" - -# command to run tests, e.g. python setup.py test -script: coverage run --timid --source sybil,tests -m py.test - -after_success: - - coveralls - -deploy: - provider: pypi - user: chrisw - password: - secure: p6Cl2uxMjdZRAz8VX+TMSFymCTVnFSA9LycOH9ZUEnbzDtqwnDV8lzZR48NXrMO1xDZ5Q4/qJm2UDh5ulMd60hotXM7IvUBCTQN1z1FwIJwig0xUrz3qKavuYehSsJ0ow+fdFKN1W4Y1wWl9pltuQ9OUUVKFR4AGZ1pdvT56O8+/cwHA/CbEO8LrRnLjoj3xfVoo+v8mUF6Z1ETQ28TV6HbROM6jKCcQ3Ol5IVpxqgbGH5GLeY9zE1Kua9wfkuBKYyIG0wm6iOU4OaXdVOg4X1G1mdECKx6ZCB71kVy6JeimNMBjzbs45zNj/IRxC5cFXNDDKn19HqBC+aQNsMh1+dgwC3LgJXyysVlY+M/Uic4ZsFvsZ3AbDQRVEneivfqh+KMLzY8PoJOgqcx4MNTU4brxuzfiu/KS3IF69xBfXSmsu2UOi/ePFidDHfNhdsa8+NBbYQrJYJIxbIm/YPE/prDVqR3eAsQZBtyE+6yTecvmqu1mmyYjvzcrQ27/n1NS/L1g6I8BZjRFws4WlSiMlVbVV9QBl/AhICwpJZHxateM7/kf+bbEGVacV/VWn9sIDOl6jMAE0IeN2gLdTO+Dj2z8tyV6c8tMFH1JeZ31CGN67oO4kb397kfDN14o7KNfFb+wxOHOzOZYwfvMYjLoPD5AdMsPbm4inpVooN5irhU= - on: - tags: true - repo: cjw296/sybil - python: "3.6" - skip_cleanup: true - distributions: "sdist bdist_wheel" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/PKG-INFO new/sybil-1.2.0/PKG-INFO --- old/sybil-1.0.9/PKG-INFO 2018-08-01 08:43:23.000000000 +0200 +++ new/sybil-1.2.0/PKG-INFO 2019-04-28 08:57:30.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: sybil -Version: 1.0.9 +Version: 1.2.0 Summary: Automated testing for the examples in your documentation. Home-page: https://github.com/cjw296/sybil Author: Chris Withers @@ -25,5 +25,6 @@ Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 Provides-Extra: build Provides-Extra: test diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/docs/api.rst new/sybil-1.2.0/docs/api.rst --- old/sybil-1.0.9/docs/api.rst 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/docs/api.rst 2019-04-28 08:57:09.000000000 +0200 @@ -18,3 +18,6 @@ .. automodule:: sybil.parsers.capture :members: + +.. automodule:: sybil.parsers.skip + :members: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/docs/changes.rst new/sybil-1.2.0/docs/changes.rst --- old/sybil-1.0.9/docs/changes.rst 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/docs/changes.rst 2019-04-28 08:57:09.000000000 +0200 @@ -1,6 +1,29 @@ Changes ======= +1.2.0 (28 Apr 2019) +------------------- + +- Only compile code in :ref:`codeblocks <codeblock-parser>` at evaluation time, + giving :ref:`skip <skip-parser>` a chance to skip code blocks that won't + compile on a particular version of Python. + +1.1.0 (25 Apr 2019) +------------------- + +- Move to CircleCI__ and Carthorse__. + + __ https://circleci.com/gh/cjw296/sybil + __ https://github.com/cjw296/carthorse + +- Add warning about the limitations of + :attr:`~sybil.parsers.doctest.FIX_BYTE_UNICODE_REPR`. + +- Support explicit filenames to include and patterns to exclude + when instantiating a :class:`~sybil.Sybil`. + +- Add the :ref:`skip <skip-parser>` parser. + 1.0.9 (1 Aug 2018) ------------------ @@ -16,7 +39,7 @@ - Literal tabs may no longer be included in text that is parsed by the :class:`~sybil.parsers.doctest.DocTestParser`. Previously, tabs were - expanded which could unpleasant problems. + expanded which could cause unpleasant problems. 1.0.6 (30 November 2017) ------------------------ @@ -54,7 +77,7 @@ ------------------- - Fix bug where unicode and byte literals weren't corrected in doctest - tracebacks, event when :attr:`sybil.parsers.doctest.FIX_BYTE_UNICODE_REPR` + tracebacks, even when :attr:`sybil.parsers.doctest.FIX_BYTE_UNICODE_REPR` was specified. 1.0.0 (26 May 2017) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/docs/conf.py new/sybil-1.2.0/docs/conf.py --- old/sybil-1.0.9/docs/conf.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/docs/conf.py 2019-04-28 08:57:09.000000000 +0200 @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- -import sys, os, pkginfo, datetime +import os, pkg_resources, datetime on_rtd = os.environ.get('READTHEDOCS', None) == 'True' -pkg_info = pkginfo.Develop(os.path.join(os.path.dirname(__file__),'..')) intersphinx_mapping = { 'https://docs.python.org/3/': None, @@ -17,14 +16,13 @@ # General source_suffix = '.rst' master_doc = 'index' -project = pkg_info.name +project = 'sybil' copyright = '2017 - %s Chris Withers' % datetime.datetime.now().year -version = release = pkg_info.version +version = release = pkg_resources.get_distribution(project).version exclude_patterns = [ 'description.rst', '_build', - 'example.rst', - 'example', + 'example*', ] pygments_style = 'sphinx' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/docs/conftest.py new/sybil-1.2.0/docs/conftest.py --- old/sybil-1.0.9/docs/conftest.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/docs/conftest.py 2019-04-28 08:57:09.000000000 +0200 @@ -3,12 +3,14 @@ from sybil.parsers.capture import parse_captures from sybil.parsers.codeblock import CodeBlockParser from sybil.parsers.doctest import DocTestParser, FIX_BYTE_UNICODE_REPR +from sybil.parsers.skip import skip pytest_collect_file = Sybil( parsers=[ DocTestParser(optionflags=ELLIPSIS|FIX_BYTE_UNICODE_REPR), CodeBlockParser(future_imports=['print_function']), parse_captures, + skip, ], pattern='*.rst', ).pytest() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/docs/example-skip.rst new/sybil-1.2.0/docs/example-skip.rst --- old/sybil-1.0.9/docs/example-skip.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/sybil-1.2.0/docs/example-skip.rst 2019-04-28 08:57:09.000000000 +0200 @@ -0,0 +1,26 @@ +.. skip: next + +This would be wrong: + +>>> 1 == 2 +True + +This is pseudo-code: + +.. skip: start + +>>> foo = ... +>>> foo(..) + +.. skip: end + +.. invisible-code-block: python + + import sys + +This will only work on Python 3: + +.. skip: next if(sys.version_info < (3, 0), reason="python 3 only") + +>>> repr(b'foo') +"b'foo'" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/docs/parsers.rst new/sybil-1.2.0/docs/parsers.rst --- old/sybil-1.0.9/docs/parsers.rst 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/docs/parsers.rst 2019-04-28 08:57:09.000000000 +0200 @@ -6,7 +6,7 @@ are as expected. A number of parsers are included, and it's simple enough to write your own. The included parsers are as follows: -.. _capture-doctest: +.. _doctest-parser: doctest ------- @@ -27,7 +27,15 @@ with the required options and passing it as an element in the list passed as the ``parsers`` parameter to :class:`~sybil.Sybil`. -.. _capture-codeblock: +.. warning:: + + :attr:`~sybil.parsers.doctest.FIX_BYTE_UNICODE_REPR` is quite simplistic. It will catch + examples but you may hit problems where, for example, ``['b', '']`` in expected + output will be rewritten as ``['', '']`` on Python 2 and ``['u', '']`` as ``['', '']``. + on Python 3. To work around this, either only run Sybil on Python 3 and do not + use this option, or pick different example output. + +.. _codeblock-parser: codeblock --------- @@ -122,6 +130,47 @@ as an element in the list passed as the ``parsers`` parameter to :class:`~sybil.Sybil`. +.. _skip-parser: + +skip +---- + +This parser takes advantage of Sphinx `comment`__ syntax to introduce +special comments that allow other examples in the document to be skipped. +This can be useful if they include pseudo code or examples that can only be +evaluated on a particular version of Python. + +__ http://www.sphinx-doc.org/en/stable/rest.html#comments + +For example: + +.. literalinclude:: example-skip.rst + :language: rest + :lines: 1-6 + +If you need to skip a collection of examples, this can be done as follows: + +.. literalinclude:: example-skip.rst + :language: rest + :lines: 8-15 + +You can also add conditions to either ``next`` or ``start`` as shown below: + +.. literalinclude:: example-skip.rst + :language: rest + :lines: 17- + +As you can see, any names used in the expression passed to ``if`` must be +present in the document's :attr:`~sybil.document.Document.namespace`. +:ref:`invisible code blocks <codeblock-parser>`, :class:`setup <sybil.Sybil>` +methods or :ref:`fixtures <pytest_integration>` are good ways to provide these. + +The parser is used by including :func:`sybil.parsers.skip.skip` +as an element in the list passed as the +``parsers`` parameter to :class:`~sybil.Sybil`. + +.. _developing-parsers: + Developing your own parsers --------------------------- @@ -135,7 +184,8 @@ :class:`~sybil.document.Document` and the :class:`~sybil.Region` and should either raise an exception or return a textual description in the event of the example not being as expected. Evaluators may also -modify the document's :attr:`~sybil.document.Document.namespace`. +modify the document's :attr:`~sybil.document.Document.namespace` +or :attr:`~sybil.document.Document.evaluator`. As an example, let's look at a parser suitable for evaluating bash commands in a subprocess and checking the output is as expected:: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/requirements.txt new/sybil-1.2.0/requirements.txt --- old/sybil-1.0.9/requirements.txt 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/requirements.txt 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ --e .[build] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/setup.py new/sybil-1.2.0/setup.py --- old/sybil-1.0.9/setup.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/setup.py 2019-04-28 08:57:09.000000000 +0200 @@ -9,7 +9,7 @@ setup( name='sybil', - version='1.0.9', + version='1.2.0', author='Chris Withers', author_email='ch...@withers.org', license='MIT', @@ -24,15 +24,16 @@ 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', ], packages=find_packages(exclude=['tests', 'functional_tests']), zip_safe=False, include_package_data=True, extras_require=dict( test=[ - 'coveralls', 'nose', 'pytest>=3.5.0', + 'pytest-cov', ], build=['sphinx', 'pkginfo', 'setuptools-git', 'twine', 'wheel'] ), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/sybil/document.py new/sybil-1.2.0/sybil/document.py --- old/sybil-1.0.9/sybil/document.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/sybil/document.py 2019-04-28 08:57:09.000000000 +0200 @@ -10,6 +10,14 @@ It will be instantiated by Sybil and provided to each parser in turn. """ + #: This can be set by :ref:`evaluators <developing-parsers>` to affect the evaluation + #: of future examples. It can be set to a callable that takes an + #: :class:`~sybil.example.Example`. This callable can then do whatever it needs to do, + #: including not executing the example at all, modifying it, or the + #: :class:`~sybil.document.Document` or calling the original evaluator on the example. + #: This last case should always take the form of ``example.region.evaluator(example)``. + evaluator = None + def __init__(self, text, path): #: This is the text of the documentation source file. self.text = text diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/sybil/example.py new/sybil-1.2.0/sybil/example.py --- old/sybil-1.0.9/sybil/example.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/sybil/example.py 2019-04-28 08:57:09.000000000 +0200 @@ -49,6 +49,7 @@ ) def evaluate(self): - result = self.region.evaluator(self) + evaluator = self.document.evaluator or self.region.evaluator + result = evaluator(self) if result: raise SybilFailure(self, result) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/sybil/integration/pytest.py new/sybil-1.2.0/sybil/integration/pytest.py --- old/sybil-1.0.9/sybil/integration/pytest.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/sybil/integration/pytest.py 2019-04-28 08:57:09.000000000 +0200 @@ -45,7 +45,7 @@ closure = fm.getfixtureclosure(names, self) try: initialnames, names_closure, arg2fixturedefs = closure - except ValueError: + except ValueError: # pragma: no cover # pytest < 3.7 names_closure, arg2fixturedefs = closure fixtureinfo = FuncFixtureInfo(names, names_closure, arg2fixturedefs) @@ -80,10 +80,7 @@ # Messier than it could be because slicing a list subclass in # Python 2 returns a list, not an instance of the subclass. tb = excinfo.traceback.cut(path=example_module_path) - try: - tb = tb[1] - except IndexError: - pass + tb = tb[1] if getattr(tb, '_rawentry', None) is not None: excinfo.traceback = Traceback(tb._rawentry, excinfo) @@ -116,7 +113,7 @@ def pytest_integration(sybil): def pytest_collect_file(parent, path): - if path.fnmatch(sybil.pattern): + if sybil.should_test_filename(path.basename): return SybilFile(path, parent, sybil) return pytest_collect_file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/sybil/parsers/codeblock.py new/sybil-1.2.0/sybil/parsers/codeblock.py --- old/sybil-1.0.9/sybil/parsers/codeblock.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/sybil/parsers/codeblock.py 2019-04-28 08:57:09.000000000 +0200 @@ -10,8 +10,13 @@ re.MULTILINE) +def compile_codeblock(source, path): + return compile(source, path, 'exec', dont_inherit=True) + + def evaluate_code_block(example): - exec(example.parsed, example.namespace) + code = compile_codeblock(example.parsed, example.document.path) + exec(code, example.namespace) # exec adds __builtins__, we don't want it: del example.namespace['__builtins__'] @@ -19,7 +24,7 @@ class CodeBlockParser(object): """ A class to instantiate and include when your documentation makes use of - :ref:`capture-codeblock` examples. + :ref:`codeblock-parser` examples. :param future_imports: An optional list of strings that will be turned into @@ -48,10 +53,9 @@ ) line_prefix = '\n' * line_count source = line_prefix + source - code = compile(source, document.path, 'exec', dont_inherit=True) yield Region( start_match.start(), source_end, - code, + source, evaluate_code_block ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/sybil/parsers/doctest.py new/sybil-1.2.0/sybil/parsers/doctest.py --- old/sybil-1.0.9/sybil/parsers/doctest.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/sybil/parsers/doctest.py 2019-04-28 08:57:09.000000000 +0200 @@ -13,8 +13,14 @@ from ..compat import PY3 from ..region import Region -BYTE_LITERAL = re.compile(r"b((['\"])[^\2]*\2)", re.MULTILINE) -UNICODE_LITERAL = re.compile(r"u((['\"])[^\2]*\2)", re.MULTILINE) + +def make_literal(literal): + return re.compile(literal+r"((['\"])[^\2]*\2)", re.MULTILINE) + + +BYTE_LITERAL = make_literal('b') +UNICODE_LITERAL = make_literal('u') + #: A :ref:`doctest option flag<option-flags-and-directives>` that #: causes byte and unicode literals in doctest expected @@ -52,7 +58,7 @@ class DocTestParser(BaseDocTestParser): """ A class to instantiate and include when your documentation makes use of - :ref:`capture-doctest` examples. + :ref:`doctest-parser` examples. :param optionflags: :ref:`doctest option flags<option-flags-and-directives>` to use diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/sybil/parsers/skip.py new/sybil-1.2.0/sybil/parsers/skip.py --- old/sybil-1.0.9/sybil/parsers/skip.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sybil-1.2.0/sybil/parsers/skip.py 2019-04-28 08:57:09.000000000 +0200 @@ -0,0 +1,76 @@ +from unittest import SkipTest +import re + +from sybil import Region + +SKIP = re.compile(r'^[ \t]*\.\.\s*skip:\s*(\w+)(?:\s+if(.+)$)?', re.MULTILINE) + + +class If: + + def __init__(self, default_reason): + self.default_reason = default_reason + + def __call__(self, condition, reason=None): + if condition: + return reason or self.default_reason + + +class Skip(object): + + def __init__(self, original_evaluator): + self.original_evaluator = original_evaluator + self.restore_next = False + self.reason = None + + def __call__(self, example): + document = example.document + + if self.restore_next: + document.evaluator = self.original_evaluator + + if example.region.evaluator is evaluate_skip: + + action, condition = example.parsed + + if condition: + + if action == 'end': + raise ValueError('Cannot have condition on end') + + namespace = document.namespace.copy() + namespace['If'] = If(condition) + reason = eval('If'+condition, namespace) + if reason: + self.reason = SkipTest(reason) + else: + document.evaluator = self.original_evaluator + return + + if action == 'next': + self.restore_next = True + elif action == 'start': + pass + elif action == 'end': + document.evaluator = self.original_evaluator + else: + raise ValueError('Bad skip action: '+action) + + elif self.reason: + raise self.reason + + +def evaluate_skip(example): + evaluator = example.document.evaluator + if not isinstance(evaluator, Skip): + example.document.evaluator = evaluator = Skip(evaluator) + evaluator(example) + + +def skip(document): + """ + A parser function to be included when your documentation makes use of + :ref:`skipping <skip-parser>` examples in a document. + """ + for match in re.finditer(SKIP, document.text): + yield Region(match.start(), match.end(), match.groups(), evaluate_skip) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/sybil/sybil.py new/sybil-1.2.0/sybil/sybil.py --- old/sybil-1.0.9/sybil/sybil.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/sybil/sybil.py 2019-04-28 08:57:09.000000000 +0200 @@ -1,10 +1,26 @@ import sys +from fnmatch import fnmatch from glob import glob +from os import listdir from os.path import join, dirname, abspath from .document import Document +class FilenameFilter(object): + + def __init__(self, pattern, filenames, excludes): + self.pattern = pattern + self.filenames = filenames + self.excludes = excludes + + def __call__(self, filename): + return ( + (fnmatch(filename, self.pattern) or filename in self.filenames) + and not any(fnmatch(filename, e) for e in self.excludes) + ) + + class Sybil(object): """ An object to provide test runner integration for discovering examples @@ -16,11 +32,22 @@ :param path: The path in which documentation source files are found, relative to the path of the Python source file in which this class is instantiated. + + .. note:: + + This is ignored when using the :ref:`pytest integration <pytest_integration>`. :param pattern: - A :mod:`glob` used to match files found in the ``path``. Matching files - will be parsed for examples. + An optional :func:`pattern <fnmatch.fnmatch>` used to match documentation source + files that will be parsed for examples. + :param filenames: + An optional :class:`set` of source file names that will be parsed for examples. + + :param excludes: + An optional sequence of :func:`patterns <fnmatch.fnmatch>` of source file names + that will excluded when looking for examples. + :param setup: An optional callable that will be called once before any examples from a :class:`~sybil.document.Document` are evaluated. If provided, it is @@ -39,8 +66,9 @@ inserted into the document's :attr:`~sybil.document.Document.namespace`. All scopes of fixture are supported. """ - def __init__(self, parsers, pattern, path='.', - setup=None, teardown=None, fixtures=()): + def __init__(self, parsers, pattern='', path='.', + setup=None, teardown=None, fixtures=(), + filenames=(), excludes=()): self.parsers = parsers calling_filename = sys._getframe(1).f_globals.get('__file__') if calling_filename: @@ -48,7 +76,7 @@ else: start_path = path self.path = abspath(start_path) - self.pattern = pattern + self.should_test_filename = FilenameFilter(pattern, filenames, excludes) self.setup = setup self.teardown = teardown self.fixtures = fixtures @@ -57,8 +85,9 @@ return Document.parse(path, *self.parsers) def all_documents(self): - for path in sorted(glob(join(self.path, self.pattern))): - yield self.parse(path) + for filename in sorted(listdir(self.path)): + if self.should_test_filename(filename): + yield self.parse(join(self.path, filename)) def pytest(self): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/sybil.egg-info/PKG-INFO new/sybil-1.2.0/sybil.egg-info/PKG-INFO --- old/sybil-1.0.9/sybil.egg-info/PKG-INFO 2018-08-01 08:43:23.000000000 +0200 +++ new/sybil-1.2.0/sybil.egg-info/PKG-INFO 2019-04-28 08:57:30.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: sybil -Version: 1.0.9 +Version: 1.2.0 Summary: Automated testing for the examples in your documentation. Home-page: https://github.com/cjw296/sybil Author: Chris Withers @@ -25,5 +25,6 @@ Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 Provides-Extra: build Provides-Extra: test diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/sybil.egg-info/SOURCES.txt new/sybil-1.2.0/sybil.egg-info/SOURCES.txt --- old/sybil-1.0.9/sybil.egg-info/SOURCES.txt 2018-08-01 08:43:23.000000000 +0200 +++ new/sybil-1.2.0/sybil.egg-info/SOURCES.txt 2019-04-28 08:57:30.000000000 +0200 @@ -1,9 +1,10 @@ +.carthorse.yml .gitignore -.travis.yml +.readthedocs.yml README.rst -requirements.txt setup.cfg setup.py +.circleci/config.yml docs/Makefile docs/api.rst docs/changes.rst @@ -11,6 +12,7 @@ docs/conftest.py docs/description.rst docs/development.rst +docs/example-skip.rst docs/example.rst docs/index.rst docs/license.rst @@ -44,6 +46,7 @@ sybil/parsers/capture.py sybil/parsers/codeblock.py sybil/parsers/doctest.py +sybil/parsers/skip.py tests/__init__.py tests/helpers.py tests/test_capture.py @@ -51,6 +54,7 @@ tests/test_doc_example.py tests/test_doctest.py tests/test_functional.py +tests/test_skip.py tests/test_sybil.py tests/functional/functional_unittest/__init__.py tests/functional/functional_unittest/test_unittest.py @@ -72,4 +76,8 @@ tests/samples/doctest_min_indent.txt tests/samples/doctest_tabs.txt tests/samples/sample1.txt -tests/samples/sample2.txt \ No newline at end of file +tests/samples/sample2.txt +tests/samples/skip-conditional-bad.txt +tests/samples/skip-conditional-edges.txt +tests/samples/skip-conditional.txt +tests/samples/skip.txt \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/sybil.egg-info/requires.txt new/sybil-1.2.0/sybil.egg-info/requires.txt --- old/sybil-1.0.9/sybil.egg-info/requires.txt 2018-08-01 08:43:23.000000000 +0200 +++ new/sybil-1.2.0/sybil.egg-info/requires.txt 2019-04-28 08:57:30.000000000 +0200 @@ -7,6 +7,6 @@ wheel [test] -coveralls nose pytest>=3.5.0 +pytest-cov diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/tests/functional/functional_unittest/test_unittest.py new/sybil-1.2.0/tests/functional/functional_unittest/test_unittest.py --- old/sybil-1.0.9/tests/functional/functional_unittest/test_unittest.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/tests/functional/functional_unittest/test_unittest.py 2019-04-28 08:57:09.000000000 +0200 @@ -21,7 +21,7 @@ def parse_for(letter, document): - for m in re.finditer('(%s+) (\d+) check' % letter, document.text): + for m in re.finditer(r'(%s+) (\d+) check' % letter, document.text): yield Region(m.start(), m.end(), (m.group(1), int(m.group(2))), partial(check, letter)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/tests/functional/nose/test_nose.py new/sybil-1.2.0/tests/functional/nose/test_nose.py --- old/sybil-1.0.9/tests/functional/nose/test_nose.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/tests/functional/nose/test_nose.py 2019-04-28 08:57:09.000000000 +0200 @@ -21,7 +21,7 @@ def parse_for(letter, document): - for m in re.finditer('(%s+) (\d+) check' % letter, document.text): + for m in re.finditer(r'(%s+) (\d+) check' % letter, document.text): yield Region(m.start(), m.end(), (m.group(1), int(m.group(2))), partial(check, letter)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/tests/functional/pytest/conftest.py new/sybil-1.2.0/tests/functional/pytest/conftest.py --- old/sybil-1.0.9/tests/functional/pytest/conftest.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/tests/functional/pytest/conftest.py 2019-04-28 08:57:09.000000000 +0200 @@ -57,7 +57,7 @@ def parse_for(letter, document): - for m in re.finditer('(%s+) (\d+) check' % letter, document.text): + for m in re.finditer(r'(%s+) (\d+) check' % letter, document.text): yield Region(m.start(), m.end(), (m.group(1), int(m.group(2))), partial(check, letter)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/tests/samples/skip-conditional-bad.txt new/sybil-1.2.0/tests/samples/skip-conditional-bad.txt --- old/sybil-1.0.9/tests/samples/skip-conditional-bad.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/sybil-1.2.0/tests/samples/skip-conditional-bad.txt 2019-04-28 08:57:09.000000000 +0200 @@ -0,0 +1,11 @@ +Bad action: + +.. skip: lolwut + +Condition on end: + +.. skip: end if(1 > 2) + +Malformed if: + +.. skip: next if(sys.version_info < (3, 0), reason="only true on python 3" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/tests/samples/skip-conditional-edges.txt new/sybil-1.2.0/tests/samples/skip-conditional-edges.txt --- old/sybil-1.0.9/tests/samples/skip-conditional-edges.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/sybil-1.2.0/tests/samples/skip-conditional-edges.txt 2019-04-28 08:57:09.000000000 +0200 @@ -0,0 +1,31 @@ +This will only work on Python 2: + +.. skip: next if(sys.version_info >= (3, 0), reason="only true on python 2") + +>>> repr(u'foo') +"u'foo'" + +This should run: + +>>> run.append(1) + +This will only parse on Python 3: + +.. skip: start if(sys.version_info < (3, 0), reason="only true on python 3") + +.. code-block:: python + + def myfunc(*, keyword_only): + return keyword_only + +>>> myfunc(keyword_only='hello') +'hello' + +>>> def myfunc2(*, keyword_only): pass + +.. skip: end + +This should also run: + +>>> run.append(2) + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/tests/samples/skip-conditional.txt new/sybil-1.2.0/tests/samples/skip-conditional.txt --- old/sybil-1.0.9/tests/samples/skip-conditional.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/sybil-1.2.0/tests/samples/skip-conditional.txt 2019-04-28 08:57:09.000000000 +0200 @@ -0,0 +1,19 @@ +>>> result.append('start') + +Default reason: + +.. skip: next if(2 > 1) + +Should not run: + +>>> result.append('bad 1') +>>> result.append('good 1') + +.. skip: start if(2 > 1, reason='foo') + +>>> result.append('bad 2') +>>> result.append('bad 3') + +.. skip: end + +>>> result.append('good 2') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/tests/samples/skip.txt new/sybil-1.2.0/tests/samples/skip.txt --- old/sybil-1.0.9/tests/samples/skip.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/sybil-1.2.0/tests/samples/skip.txt 2019-04-28 08:57:09.000000000 +0200 @@ -0,0 +1,41 @@ +.. invisible-code-block: python + + run = [] + +Let's skips some stuff: + +.. skip: next + +After this text is a code block that goes boom, it should be skipped: + +.. code-block:: python + + run.append(1) + +This one should run: + +.. invisible-code-block: python + + run.append(2) + +.. skip: start + +These should not: + +.. code-block:: python + + run.append(3) + +Nor this one: + +.. code-block:: python + + run.append(4) + +.. skip: end + +But this one should: + +.. code-block:: python + + run.append(5) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/tests/test_codeblock.py new/sybil-1.2.0/tests/test_codeblock.py --- old/sybil-1.0.9/tests/test_codeblock.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/tests/test_codeblock.py 2019-04-28 08:57:09.000000000 +0200 @@ -1,6 +1,6 @@ import pytest from sybil.compat import StringIO -from sybil.parsers.codeblock import CodeBlockParser +from sybil.parsers.codeblock import CodeBlockParser, compile_codeblock from tests.helpers import document_from_sample, evaluate_region @@ -42,11 +42,13 @@ 'pathalogical worst case for line numbers\n' ) # the future import line drops the firstlineno by 1 - assert regions[0].parsed.co_firstlineno == 2 + code = compile_codeblock(regions[0].parsed, document.path) + assert code.co_firstlineno == 2 assert evaluate_region(regions[1], namespace) is None assert buffer.getvalue() == ( 'pathalogical worst case for line numbers\n' 'still should work and have good line numbers\n' ) # the future import line drops the firstlineno by 1 - assert regions[1].parsed.co_firstlineno == 8 + code = compile_codeblock(regions[1].parsed, document.path) + assert code.co_firstlineno == 8 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/tests/test_doctest.py new/sybil-1.2.0/tests/test_doctest.py --- old/sybil-1.0.9/tests/test_doctest.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/tests/test_doctest.py 2019-04-28 08:57:09.000000000 +0200 @@ -54,11 +54,8 @@ document = document_from_sample('doctest_literals.txt') regions = list(DocTestParser(FIX_BYTE_UNICODE_REPR)(document)) assert len(regions) == 5 - assert evaluate_region(regions[0], {}) == '' - assert evaluate_region(regions[1], {}) == '' - assert evaluate_region(regions[2], {}) == '' - assert evaluate_region(regions[3], {}) == '' - assert evaluate_region(regions[4], {}) == '' + for region in regions: + assert evaluate_region(region, {}) == '' def test_min_indent(): @@ -68,6 +65,7 @@ namespace = document.namespace assert evaluate_region(regions[0], namespace) == '' + def test_tabs(): path = sample_path('doctest_tabs.txt') parser = DocTestParser(optionflags=REPORT_NDIFF|ELLIPSIS) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/tests/test_functional.py new/sybil-1.2.0/tests/test_functional.py --- old/sybil-1.0.9/tests/test_functional.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/tests/test_functional.py 2019-04-28 08:57:09.000000000 +0200 @@ -27,7 +27,7 @@ self.session = session results = CollectResults() - return_code = pytest_main(['-vs', join(functional_test_dir, 'pytest')], + return_code = pytest_main(['-vvs', join(functional_test_dir, 'pytest')], plugins=[results]) assert return_code == 1 assert results.session.testsfailed == 4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/tests/test_skip.py new/sybil-1.2.0/tests/test_skip.py --- old/sybil-1.0.9/tests/test_skip.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sybil-1.2.0/tests/test_skip.py 2019-04-28 08:57:09.000000000 +0200 @@ -0,0 +1,77 @@ +import sys +from unittest import SkipTest + +import pytest + +from sybil.compat import PY3 +from sybil.document import Document +from sybil.parsers.codeblock import CodeBlockParser +from sybil.parsers.doctest import DocTestParser +from sybil.parsers.skip import skip + +from .helpers import sample_path, document_from_sample, evaluate_region + + +def test_basic(): + document = Document.parse(sample_path('skip.txt'), CodeBlockParser(), skip) + for example in document: + example.evaluate() + assert document.namespace['run'] == [2, 5] + + +def test_conditional_edge_cases(): + document = Document.parse( + sample_path('skip-conditional-edges.txt'), + CodeBlockParser(), DocTestParser(), skip + ) + document.namespace['sys'] = sys + document.namespace['run'] = [] + skipped = [] + for example in document: + try: + example.evaluate() + except SkipTest as e: + skipped.append(str(e)) + assert document.namespace['run'] == [1, 2] + # we should always have one and only one skip from this document. + if PY3: + assert skipped == ['only true on python 2'] + else: + assert skipped == ['only true on python 3'] * 3 + + +def test_conditional_full(): + document = Document.parse( + sample_path('skip-conditional.txt'), DocTestParser(), skip + ) + document.namespace['result'] = result = [] + for example in document: + try: + example.evaluate() + except SkipTest as e: + result.append('skip:'+str(e)) + assert result == [ + 'start', + 'skip:(2 > 1)', + 'good 1', + 'skip:foo', + 'skip:foo', + 'good 2', + ] + + +def test_bad(): + document = document_from_sample('skip-conditional-bad.txt') + regions = list(skip(document)) + namespace = document.namespace + + with pytest.raises(ValueError) as excinfo: + evaluate_region(regions[0], namespace) + assert str(excinfo.value) == 'Bad skip action: lolwut' + + with pytest.raises(ValueError) as excinfo: + evaluate_region(regions[1], namespace) + assert str(excinfo.value) == 'Cannot have condition on end' + + with pytest.raises(SyntaxError): + evaluate_region(regions[2], namespace) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sybil-1.0.9/tests/test_sybil.py new/sybil-1.2.0/tests/test_sybil.py --- old/sybil-1.0.9/tests/test_sybil.py 2018-08-01 08:42:42.000000000 +0200 +++ new/sybil-1.2.0/tests/test_sybil.py 2019-04-28 08:57:09.000000000 +0200 @@ -174,14 +174,14 @@ def parse_for_x(document): - for m in re.finditer('(X+) (\d+) check', document.text): + for m in re.finditer(r'(X+) (\d+) check', document.text): yield Region(m.start(), m.end(), (m.group(1), int(m.group(2))), partial(check, 'X')) def parse_for_y(document): - for m in re.finditer('(Y+) (\d+) check', document.text): + for m in re.finditer(r'(Y+) (\d+) check', document.text): yield Region(m.start(), m.end(), (m.group(1), int(m.group(2))), partial(check, 'Y')) @@ -225,6 +225,24 @@ 'Y count was 3, as expected']) +class TestFiltering(object): + + def check(self, sybil, expected): + assert expected == [split(d.path)[-1] for d in sybil.all_documents()] + + def test_excludes(self, tmp_path): + (tmp_path / 'foo.txt').write_text(u'') + (tmp_path / 'bar.txt').write_text(u'') + sybil = Sybil([], path=str(tmp_path), pattern='*.txt', excludes=['bar.txt']) + self.check(sybil, expected=['foo.txt']) + + def test_filenames(self, tmp_path): + (tmp_path / 'foo.txt').write_text(u'') + (tmp_path / 'bar.txt').write_text(u'') + sybil = Sybil([], path=str(tmp_path), filenames=['bar.txt']) + self.check(sybil, expected=['bar.txt']) + + def check_into_namespace(example): parsed, namespace = example.region.parsed, example.namespace if 'parsed' not in namespace: @@ -234,7 +252,7 @@ def parse(document): - for m in re.finditer('([XY]+) (\d+) check', document.text): + for m in re.finditer(r'([XY]+) (\d+) check', document.text): yield Region(m.start(), m.end(), m.start(), check_into_namespace)