diff -Nru manuel-1.10.1/badges/coverage-badge.svg manuel-1.12.4/badges/coverage-badge.svg
--- manuel-1.10.1/badges/coverage-badge.svg 1970-01-01 00:00:00.000000000 +0000
+++ manuel-1.12.4/badges/coverage-badge.svg 2022-06-24 11:50:26.000000000 +0000
@@ -0,0 +1 @@
+
\ No newline at end of file
diff -Nru manuel-1.10.1/bin/genbadge manuel-1.12.4/bin/genbadge
--- manuel-1.10.1/bin/genbadge 1970-01-01 00:00:00.000000000 +0000
+++ manuel-1.12.4/bin/genbadge 2022-06-24 11:50:26.000000000 +0000
@@ -0,0 +1,24 @@
+"""This is a hack to get the coverage percentage reported in whole numbers."""
+import re
+import sys
+from genbadge import utils_coverage
+from genbadge import utils_badge
+
+def my_get_coverage_badge(cov_stats):
+ """Generate a coverage badge they way I like it.
+
+ The original included two decimal places in the percentage.
+ I just want an integer percentage.
+ """
+
+ color = utils_coverage.get_color(cov_stats)
+ right_txt = '%.0f%%' % (cov_stats.total_coverage,)
+ return utils_badge.Badge(left_txt="coverage", right_txt=right_txt, color=color)
+
+utils_coverage.get_coverage_badge = my_get_coverage_badge
+
+import genbadge.main
+
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(genbadge.main.genbadge())
diff -Nru manuel-1.10.1/bootstrap.py manuel-1.12.4/bootstrap.py
--- manuel-1.10.1/bootstrap.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/bootstrap.py 1970-01-01 00:00:00.000000000 +0000
@@ -1,178 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Bootstrap a buildout-based project
-
-Simply run this script in a directory containing a buildout.cfg.
-The script accepts buildout command-line options, so you can
-use the -c option to specify an alternate configuration file.
-"""
-
-import os
-import shutil
-import sys
-import tempfile
-
-from optparse import OptionParser
-
-tmpeggs = tempfile.mkdtemp()
-
-usage = '''\
-[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
-
-Bootstraps a buildout-based project.
-
-Simply run this script in a directory containing a buildout.cfg, using the
-Python that you want bin/buildout to use.
-
-Note that by using --find-links to point to local resources, you can keep
-this script from going over the network.
-'''
-
-parser = OptionParser(usage=usage)
-parser.add_option("-v", "--version", help="use a specific zc.buildout version")
-
-parser.add_option("-t", "--accept-buildout-test-releases",
- dest='accept_buildout_test_releases',
- action="store_true", default=False,
- help=("Normally, if you do not specify a --version, the "
- "bootstrap script and buildout gets the newest "
- "*final* versions of zc.buildout and its recipes and "
- "extensions for you. If you use this flag, "
- "bootstrap and buildout will get the newest releases "
- "even if they are alphas or betas."))
-parser.add_option("-c", "--config-file",
- help=("Specify the path to the buildout configuration "
- "file to be used."))
-parser.add_option("-f", "--find-links",
- help=("Specify a URL to search for buildout releases"))
-parser.add_option("--allow-site-packages",
- action="store_true", default=False,
- help=("Let bootstrap.py use existing site packages"))
-
-
-options, args = parser.parse_args()
-
-######################################################################
-# load/install setuptools
-
-try:
- if options.allow_site_packages:
- import setuptools
- import pkg_resources
- from urllib.request import urlopen
-except ImportError:
- from urllib2 import urlopen
-
-ez = {}
-exec(urlopen('https://bootstrap.pypa.io/ez_setup.py').read(), ez)
-
-if not options.allow_site_packages:
- # ez_setup imports site, which adds site packages
- # this will remove them from the path to ensure that incompatible versions
- # of setuptools are not in the path
- import site
- # inside a virtualenv, there is no 'getsitepackages'.
- # We can't remove these reliably
- if hasattr(site, 'getsitepackages'):
- for sitepackage_path in site.getsitepackages():
- sys.path[:] = [x for x in sys.path if sitepackage_path not in x]
-
-setup_args = dict(to_dir=tmpeggs, download_delay=0)
-ez['use_setuptools'](**setup_args)
-import setuptools
-import pkg_resources
-
-# This does not (always?) update the default working set. We will
-# do it.
-for path in sys.path:
- if path not in pkg_resources.working_set.entries:
- pkg_resources.working_set.add_entry(path)
-
-######################################################################
-# Install buildout
-
-ws = pkg_resources.working_set
-
-cmd = [sys.executable, '-c',
- 'from setuptools.command.easy_install import main; main()',
- '-mZqNxd', tmpeggs]
-
-find_links = os.environ.get(
- 'bootstrap-testing-find-links',
- options.find_links or
- ('http://downloads.buildout.org/'
- if options.accept_buildout_test_releases else None)
- )
-if find_links:
- cmd.extend(['-f', find_links])
-
-setuptools_path = ws.find(
- pkg_resources.Requirement.parse('setuptools')).location
-
-requirement = 'zc.buildout'
-version = options.version
-if version is None and not options.accept_buildout_test_releases:
- # Figure out the most recent final version of zc.buildout.
- import setuptools.package_index
- _final_parts = '*final-', '*final'
-
- def _final_version(parsed_version):
- for part in parsed_version:
- if (part[:1] == '*') and (part not in _final_parts):
- return False
- return True
- index = setuptools.package_index.PackageIndex(
- search_path=[setuptools_path])
- if find_links:
- index.add_find_links((find_links,))
- req = pkg_resources.Requirement.parse(requirement)
- if index.obtain(req) is not None:
- best = []
- bestv = None
- for dist in index[req.project_name]:
- distv = dist.parsed_version
- if _final_version(distv):
- if bestv is None or distv > bestv:
- best = [dist]
- bestv = distv
- elif distv == bestv:
- best.append(dist)
- if best:
- best.sort()
- version = best[-1].version
-if version:
- requirement = '=='.join((requirement, version))
-cmd.append(requirement)
-
-import subprocess
-if subprocess.call(cmd, env=dict(os.environ, PYTHONPATH=setuptools_path)) != 0:
- raise Exception(
- "Failed to execute command:\n%s" % repr(cmd)[1:-1])
-
-######################################################################
-# Import and run buildout
-
-ws.add_entry(tmpeggs)
-ws.require(requirement)
-import zc.buildout.buildout
-
-if not [a for a in args if '=' not in a]:
- args.append('bootstrap')
-
-# if -c was provided, we push it back into args for buildout' main function
-if options.config_file is not None:
- args[0:0] = ['-c', options.config_file]
-
-zc.buildout.buildout.main(args)
-shutil.rmtree(tmpeggs)
diff -Nru manuel-1.10.1/buildout.cfg manuel-1.12.4/buildout.cfg
--- manuel-1.10.1/buildout.cfg 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/buildout.cfg 1970-01-01 00:00:00.000000000 +0000
@@ -1,73 +0,0 @@
-[buildout]
-develop = .
-parts = test interpreter sphinx-docs-html build-docs
-#allow-picked-versions = false
-use-dependency-links = false
-
-[test]
-recipe = zc.recipe.testrunner
-eggs = manuel [tests]
-defaults = '--tests-pattern tests --exit-with-status -1 --auto-color'.split()
-working-directory = ${buildout:directory}
-
-[interpreter]
-recipe = zc.recipe.egg
-eggs = manuel
-interpreter = py
-
-# generate a script that will build the user docs (HTML)
-[sphinx-docs-html]
-recipe = zc.recipe.egg:script
-eggs =
- docutils
- Sphinx
- setuptools
-scripts = sphinx-build=docs
-base-sphinx-args = ('-N -c ${buildout:directory}/sphinx ${buildout:directory}/src/manuel ${buildout:directory}/docs'.split())
-arguments = sys.argv + ${sphinx-docs-html:base-sphinx-args}
-initialization =
-
-# build the (HTML) user docs each time the buildout is run
-[build-docs]
-recipe = iw.recipe.cmd
-on_install = true
-on_update = true
-cmds = ${buildout:directory}/bin/docs
-
-[versions]
-Babel = 2.4.0
-Jinja2 = 2.9.6
-MarkupSafe = 1.0
-Pygments = 2.2.0
-Sphinx = 1.5.5
-alabaster = 0.7.10
-docutils = 0.13.1
-iw.recipe.cmd = 0.3
-pytz = 2016.10
-requests = 2.13.0
-snowballstemmer = 1.2.1
-zc.buildout = 2.9.4
-zc.recipe.egg = 2.0.4
-zc.recipe.testrunner = 2.0.0
-zope.testing = 4.6.2
-
-# Required by:
-# Sphinx==1.5.5
-imagesize = 0.7.1
-
-# Required by:
-# manuel==0
-# zope.testrunner==4.7.0
-six = 1.10.0
-
-# Required by:
-# zope.testrunner==4.7.0
-zope.exceptions = 4.1.0
-
-# Required by:
-# zope.testrunner==4.7.0
-zope.interface = 4.4.2
-
-# Required by:
-# zc.recipe.testrunner==2.0.0
-zope.testrunner = 4.7.0
diff -Nru manuel-1.10.1/CHANGES.rst manuel-1.12.4/CHANGES.rst
--- manuel-1.10.1/CHANGES.rst 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/CHANGES.rst 2022-06-24 11:50:26.000000000 +0000
@@ -1,18 +1,55 @@
CHANGES
=======
+1.12.4 (2022-06-24)
+-------------------
+
+- Remove silly PyPI version badge.
+
+
+1.12.3 (2022-06-24)
+-------------------
+
+- Modernize internal project structure; drop tox; no user-visible changes (hopefully).
+- Rework coverage badge generation.
+- Drop Travis CI badge (the project is using GitHub for CI now)
+
+
+1.11.2 (2022-05-15)
+-------------------
+
+Fix missing file in release.
+
+
+1.11.1 (2022-05-14)
+-------------------
+
+Fix brown-bag release.
+
+
+1.11.0 (2022-05-14)
+-------------------
+
+- Fix test detection in Python 2 which was broken since 1.10.0.
+ (`#20 `_)
+- Add Python 3.9 and 3.10 to tox config.
+- Add a Makefile to centeralized development activities.
+
+
1.10.1 (2018-11-15)
-------------------
- Add support for PyPy3.
+
1.10.0 (2018-11-14)
-------------------
- Fix DeprecationWarning about 'U' mode under Python 3.
-- Drop Python 2.7 and 3.3 support. Add testing and support for Python 3.6 and
+- Drop Python 2.6 and 3.3 support. Add testing and support for Python 3.6 and
3.7.
+
1.9.0 (2017-11-20)
------------------
@@ -23,6 +60,7 @@
- Added support for Python 3.5 and Python 3.6.
- Dropped support for Python 2.6
+
1.8.0 (2014-07-15)
------------------
@@ -32,6 +70,7 @@
- Fix odd ImportError problems when used with tox and coverage.
- Fix parsing of reST codeblock options with hyphens.
+
1.7.2 (2013-03-16)
------------------
diff -Nru manuel-1.10.1/.circleci/config.yml manuel-1.12.4/.circleci/config.yml
--- manuel-1.10.1/.circleci/config.yml 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/.circleci/config.yml 1970-01-01 00:00:00.000000000 +0000
@@ -1,20 +0,0 @@
-# Python CircleCI 2.0 configuration file
-#
-# Check https://circleci.com/docs/2.0/language-python/ for more details
-#
-version: 2
-
-jobs:
- build:
- working_directory: ~/repo
-
- docker:
- - image: circleci/python:3.6.4
-
- steps:
- - checkout
-
- - run:
- name: Run Tests
- command: |
- python setup.py test
diff -Nru manuel-1.10.1/constraints.txt manuel-1.12.4/constraints.txt
--- manuel-1.10.1/constraints.txt 1970-01-01 00:00:00.000000000 +0000
+++ manuel-1.12.4/constraints.txt 2022-06-24 11:50:26.000000000 +0000
@@ -0,0 +1,48 @@
+altgraph==0.17.2
+astroid==2.11.2
+black==22.3.0
+bleach==5.0.0
+certifi==2022.6.15
+charset-normalizer==2.0.12
+click==8.1.2
+commonmark==0.9.1
+coverage==6.4.1
+defusedxml==0.7.1
+dill==0.3.4
+docutils==0.18.1
+flake8==4.0.1
+genbadge==1.0.6
+idna==3.3
+importlib-metadata==4.11.4
+isort==5.10.1
+keyring==23.6.0
+lazy-object-proxy==1.7.1
+macholib==1.16
+mccabe==0.6.1
+mypy==0.942
+mypy-extensions==0.4.3
+pathspec==0.9.0
+Pillow==9.1.1
+pkginfo==1.8.3
+platformdirs==2.5.2
+pycodestyle==2.8.0
+pydocstyle==6.1.1
+pyflakes==2.4.0
+Pygments==2.12.0
+pyinstaller==5.0
+pyinstaller-hooks-contrib==2022.4
+pylint==2.13.5
+readme-renderer==35.0
+requests==2.28.0
+requests-toolbelt==0.9.1
+rfc3986==2.0.0
+rich==12.4.4
+six==1.16.0
+snowballstemmer==2.2.0
+tomli==2.0.1
+twine==4.0.1
+typing_extensions==4.2.0
+urllib3==1.26.9
+webencodings==0.5.1
+wrapt==1.14.0
+zipp==3.8.0
diff -Nru manuel-1.10.1/debian/changelog manuel-1.12.4/debian/changelog
--- manuel-1.10.1/debian/changelog 2022-05-28 00:12:28.000000000 +0000
+++ manuel-1.12.4/debian/changelog 2022-12-02 20:15:39.000000000 +0000
@@ -1,3 +1,27 @@
+manuel (1.12.4-2) unstable; urgency=medium
+
+ * Handle test output change on python3.11 (Closes: #1024956)
+
+ -- James Valleroy Fri, 02 Dec 2022 15:15:39 -0500
+
+manuel (1.12.4-1) unstable; urgency=medium
+
+ [ Carl Suster ]
+ * d/watch: switch to version 4 and track tags rather than the empty releases.
+
+ [ James Valleroy ]
+ * Test only supported python versions
+ * Set Standards-Version to 4.6.1
+ * Add my copyright years
+ * New upstream version 1.12.4
+ * Build-depend on python3-myst-parser
+ * Build-depend on python3-sphinx-copybutton
+ * Drop removed file from d/copyright
+ * Update upstream copyright years
+ * gbp: Enable pristine-tar and sign-tags
+
+ -- James Valleroy Fri, 07 Oct 2022 09:29:43 -0400
+
manuel (1.10.1-4) unstable; urgency=medium
[ Debian Janitor ]
diff -Nru manuel-1.10.1/debian/control manuel-1.12.4/debian/control
--- manuel-1.10.1/debian/control 2022-05-28 00:12:28.000000000 +0000
+++ manuel-1.12.4/debian/control 2022-12-02 20:15:39.000000000 +0000
@@ -8,11 +8,13 @@
debhelper-compat (= 13),
dh-python,
python3-all,
+ python3-myst-parser,
python3-setuptools,
python3-six,
python3-sphinx,
+ python3-sphinx-copybutton,
python3-zope.testing
-Standards-Version: 4.6.0
+Standards-Version: 4.6.1
Homepage: https://pythonhosted.org/manuel/
Vcs-Git: https://salsa.debian.org/python-team/packages/manuel.git
Vcs-Browser: https://salsa.debian.org/python-team/packages/manuel
diff -Nru manuel-1.10.1/debian/copyright manuel-1.12.4/debian/copyright
--- manuel-1.10.1/debian/copyright 2022-05-28 00:12:28.000000000 +0000
+++ manuel-1.12.4/debian/copyright 2022-12-02 20:15:39.000000000 +0000
@@ -4,16 +4,13 @@
Source: https://github.com/benji-york/manuel/
Files: *
-Copyright: 2008-2014 Benji York and contributors
+Copyright: 2008-2022 Benji York and contributors
License: Apache-2.0
-Files: bootstrap.py
-Copyright: 2006 Zope Foundation and Contributors
-License: Zope-2.1
-
Files: debian/*
Copyright: 2015 Daniel Stender
W. Martin Borgert
+ 2018-2022 James Valleroy
License: Apache-2.0
License: Apache-2.0
@@ -31,49 +28,3 @@
.
On Debian systems, the full text of the Apache License, Version 2.0
can be found in the file `/usr/share/common-licenses/Apache-2.0'.`
-
-License: Zope-2.1
- Zope Public License (ZPL) Version 2.1
- .
- A copyright notice accompanies this license document that identifies the
- copyright holders.
- .
- This license has been certified as open source. It has also been designated as
- GPL compatible by the Free Software Foundation (FSF).
- .
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- .
- 1. Redistributions in source code must retain the accompanying copyright
- notice, this list of conditions, and the following disclaimer.
- .
- 2. Redistributions in binary form must reproduce the accompanying copyright
- notice, this list of conditions, and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- .
- 3. Names of the copyright holders must not be used to endorse or promote
- products derived from this software without prior written permission from the
- copyright holders.
- .
- 4. The right to distribute this software or to use it for any purpose does not
- give you the right to use Servicemarks (sm) or Trademarks (tm) of the
- copyright
- holders. Use of them is covered by separate agreement with the copyright
- holders.
- .
- 5. If any files are modified, you must cause the modified files to carry
- prominent notices stating that you changed the files and the date of any
- change.
- .
- Disclaimer
- .
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
- OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff -Nru manuel-1.10.1/debian/gbp.conf manuel-1.12.4/debian/gbp.conf
--- manuel-1.10.1/debian/gbp.conf 2022-05-28 00:12:28.000000000 +0000
+++ manuel-1.12.4/debian/gbp.conf 2022-12-02 20:15:39.000000000 +0000
@@ -1,2 +1,6 @@
[DEFAULT]
debian-branch=debian/master
+pristine-tar = True
+
+[import-ref]
+sign-tags = True
diff -Nru manuel-1.10.1/debian/patches/0001-docs-no-updated-timstamp.patch manuel-1.12.4/debian/patches/0001-docs-no-updated-timstamp.patch
--- manuel-1.10.1/debian/patches/0001-docs-no-updated-timstamp.patch 2022-05-28 00:12:28.000000000 +0000
+++ manuel-1.12.4/debian/patches/0001-docs-no-updated-timstamp.patch 2022-12-02 20:15:39.000000000 +0000
@@ -11,10 +11,10 @@
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/conf.py b/sphinx/conf.py
-index 3d07652..8501900 100644
+index 0ffff7c..5ed7fc5 100644
--- a/sphinx/conf.py
+++ b/sphinx/conf.py
-@@ -7,7 +7,7 @@ release = '1'
+@@ -11,7 +11,7 @@ release = '1'
today_fmt = '%Y-%m-%d'
pygments_style = 'sphinx'
diff -Nru manuel-1.10.1/debian/patches/0002-Handle-test-output-change-on-python3.11.patch manuel-1.12.4/debian/patches/0002-Handle-test-output-change-on-python3.11.patch
--- manuel-1.10.1/debian/patches/0002-Handle-test-output-change-on-python3.11.patch 1970-01-01 00:00:00.000000000 +0000
+++ manuel-1.12.4/debian/patches/0002-Handle-test-output-change-on-python3.11.patch 2022-12-02 20:15:39.000000000 +0000
@@ -0,0 +1,31 @@
+From: James Valleroy
+Date: Fri, 2 Dec 2022 13:54:58 -0500
+Subject: Handle test output change on python3.11
+
+On Python 3.11, the test output is changed slightly.
+
+This patch is taken from an open upstream pull request.
+
+Closes: #1024956
+
+Forwarded: https://github.com/benji-york/manuel/pull/32
+---
+ src/manuel/index.txt | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+diff --git a/src/manuel/index.txt b/src/manuel/index.txt
+index 35247fc..0224c53 100644
+--- a/src/manuel/index.txt
++++ b/src/manuel/index.txt
+@@ -211,10 +211,7 @@ When tests are run this way:
+
+ >>> sys.stdout.writeln = lambda s: sys.stdout.write(s+'\n')
+ >>> suite = loader.loadTestsFromTestCase(MyTest)
+- >>> result = suite.run(unittest.TextTestResult(sys.stdout, True, 3))
+- test1 (tests.MyTest) ... ok
+- test2 (tests.MyTest) ... ok
+- test3 (tests.MyTest) ... FAIL
++ >>> result = suite.run(unittest.TestResult(True, 3))
+
+ >>> for _, e in result.errors:
+ ... print(e); print
diff -Nru manuel-1.10.1/debian/patches/series manuel-1.12.4/debian/patches/series
--- manuel-1.10.1/debian/patches/series 2022-05-28 00:12:28.000000000 +0000
+++ manuel-1.12.4/debian/patches/series 2022-12-02 20:15:39.000000000 +0000
@@ -1 +1,2 @@
0001-docs-no-updated-timstamp.patch
+0002-Handle-test-output-change-on-python3.11.patch
diff -Nru manuel-1.10.1/debian/tests/python3-manuel manuel-1.12.4/debian/tests/python3-manuel
--- manuel-1.10.1/debian/tests/python3-manuel 2022-05-28 00:12:28.000000000 +0000
+++ manuel-1.12.4/debian/tests/python3-manuel 2022-12-02 20:15:39.000000000 +0000
@@ -1,2 +1,2 @@
#!/bin/sh -e
-py3versions -i | tr ' ' '\n' | xargs -I {} env {} -Wd setup.py test 2>&1
+py3versions -s | tr ' ' '\n' | xargs -I {} env {} -Wd setup.py test 2>&1
diff -Nru manuel-1.10.1/debian/watch manuel-1.12.4/debian/watch
--- manuel-1.10.1/debian/watch 2022-05-28 00:12:28.000000000 +0000
+++ manuel-1.12.4/debian/watch 2022-12-02 20:15:39.000000000 +0000
@@ -1,4 +1,5 @@
-version=3
-opts="filenamemangle=s/(?:.*\/)?(\d[\d\.]+)\.tar\.gz/manuel-$1.tar.gz/" \
-https://github.com/benji-york/manuel/releases (?:.*/)?(\d[\d\.]+)\.tar\.gz
+version=4
+opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*@ARCHIVE_EXT@)%@PACKAGE@-$1%" \
+ https://github.com/benji-york/manuel/tags \
+ (?:.*?/)?v?@ANY_VERSION@@ARCHIVE_EXT@
# better than Pypi tarball (contains complete prebuild documentation)
diff -Nru manuel-1.10.1/.github/workflows/check.yml manuel-1.12.4/.github/workflows/check.yml
--- manuel-1.10.1/.github/workflows/check.yml 1970-01-01 00:00:00.000000000 +0000
+++ manuel-1.12.4/.github/workflows/check.yml 2022-06-24 11:50:26.000000000 +0000
@@ -0,0 +1,20 @@
+name: Check project
+on: push
+
+jobs:
+ build:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-18.04, ubuntu-20.04, ubuntu-latest, macos-latest]
+ python-version: ['3.10', '3.9', '3.8']
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Python
+ uses: actions/setup-python@v3
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Build project
+ run: make
+ - name: Check project
+ run: make check
diff -Nru manuel-1.10.1/.gitignore manuel-1.12.4/.gitignore
--- manuel-1.10.1/.gitignore 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/.gitignore 2022-06-24 11:50:26.000000000 +0000
@@ -1,5 +1,4 @@
.installed.cfg
-bin/
develop-eggs/
dist/
docs/
@@ -7,7 +6,8 @@
parts/
*.pyc
__pycache__/
-.tox/
.coverage
coverage.xml
+.eggs/
htmlcov
+ve/
diff -Nru manuel-1.10.1/Makefile manuel-1.12.4/Makefile
--- manuel-1.10.1/Makefile 1970-01-01 00:00:00.000000000 +0000
+++ manuel-1.12.4/Makefile 2022-06-24 11:50:26.000000000 +0000
@@ -0,0 +1,199 @@
+SHELL := bash
+.SHELLFLAGS := -eux -o pipefail -c
+.DEFAULT_GOAL := build
+.DELETE_ON_ERROR: # If a recipe to build a file exits with an error, delete the file.
+.SUFFIXES: # Remove the default suffixes which are for compiling C projects.
+.NOTPARALLEL: # Disable use of parallel subprocesses.
+MAKEFLAGS += --warn-undefined-variables
+MAKEFLAGS += --no-builtin-rules
+
+
+export COLUMNS ?= 70
+seperator ?= $(shell printf %${COLUMNS}s | tr " " "═")
+
+platform := $(shell python -c 'import sys; print(sys.platform)')
+
+PYTHON_VERSION ?= 3
+
+export PIP_DISABLE_PIP_VERSION_CHECK=1
+pip-install := ve/bin/pip --no-input install --constraint constraints.txt
+pip-check := ve/bin/pip show -q
+
+source_code := src
+
+isort := ve/bin/isort --multi-line=VERTICAL_HANGING_INDENT --trailing-comma --no-sections
+
+########################################################################################
+# Build targets
+#
+# It is acceptable for other targets to implicitly depend on these targets having been
+# run. I.e., it is ok if "make lint" generates an error before "make" has been run.
+
+.PHONY: build
+build: ve development-utilities
+
+ve:
+ python$(PYTHON_VERSION) -m venv ve
+
+ve/bin/genbadge:
+ $(pip-install) genbadge[coverage]
+
+ve/bin/%:
+ # Install development utility "$*"
+ $(pip-install) $*
+
+# Utilities we use during development.
+.PHONY: development-utilities
+development-utilities: ve/bin/black
+development-utilities: ve/bin/coverage
+development-utilities: ve/bin/flake8
+development-utilities: ve/bin/genbadge
+development-utilities: ve/bin/isort
+development-utilities: ve/bin/mypy
+development-utilities: ve/bin/pydocstyle
+development-utilities: ve/bin/pyinstaller
+development-utilities: ve/bin/pylint
+development-utilities: ve/bin/twine
+development-utilities: ve/bin/wheel
+
+########################################################################################
+# Distribution targets
+
+.PHONY: assert-one-dist
+assert-one-dist:
+ @if [ $$(find dist -name 'manuel-*.tar.gz' | wc -l) != 1 ]; then \
+ echo There must be one and only one distribution file present.; \
+ exit 1; \
+ fi
+
+.PHONY: assert-no-unreleased-changes
+assert-no-unreleased-changes:
+ @if grep unreleased CHANGES.rst > /dev/null; then \
+ echo There must not be any unreleased changes in CHANGES.rst.; \
+ exit 1; \
+ fi
+
+.PHONY: assert-version-in-changelog
+assert-version-in-changelog:
+ @if ! grep $$(ve/bin/python setup.py --version) CHANGES.rst; then \
+ echo The current version number must be mentioned in CHANGES.rst.; \
+ exit 1; \
+ fi
+
+.PHONY: assert-matching-versions
+assert-matching-versions:
+ # verify that the top-most version in the change log matches what is in setup.py
+ @env \
+ CHANGE_LOG_VERSION=$$(grep '^[^ ]\+ (20\d\d-\d\d-\d\d)' CHANGES.rst | head -n 1 | cut -d' ' -f1) \
+ SETUP_VERSION=$$(ve/bin/python setup.py --version) \
+ bash -c 'test $$CHANGE_LOG_VERSION = $$SETUP_VERSION'
+
+.PHONY: assert-no-changes
+assert-no-changes:
+ @if ! output=$$(git status --porcelain) || [ -n "$$output" ]; then \
+ echo There must not be any ucomitted changes.; \
+ exit 1; \
+ fi
+
+.PHONY: dist
+dist:
+ ve/bin/python setup.py sdist
+
+.PHONY: test-dist
+test-dist:
+ # check to see if the distribution passes the tests
+ rm -rf tmp
+ mkdir tmp
+ tar xzvf $$(find dist -name 'manuel-*.tar.gz') -C tmp
+ cd tmp/manuel-* && make && make check
+ rm -rf tmp
+
+.PHONY: upload
+upload: assert-one-dist
+ ve/bin/twine upload --repository manuel $$(find dist -name 'manuel-*.tar.gz')
+
+.PHONY: badges
+badges:
+ ve/bin/python bin/genbadge coverage -i coverage.xml -o badges/coverage-badge.svg
+
+.PHONY: release
+ifeq '$(shell git rev-parse --abbrev-ref HEAD)' 'master'
+release: clean-dist assert-no-unreleased-changes assert-matching-versions \
+ assert-version-in-changelog badges dist assert-one-dist test-dist \
+ assert-no-changes upload
+ # now that a release has happened, tag the current HEAD as that release
+ git tag $$(ve/bin/python setup.py --version)
+ git push origin
+ git push origin --tags
+else
+release:
+ @echo Error: must be on master branch to do a release.; exit 1
+endif
+
+########################################################################################
+# Test and lint targets
+
+.PHONY: pylint
+pylint:
+ ve/bin/pylint $(source_code) --output-format=colorized
+
+.PHONY: flake8
+flake8:
+ ve/bin/flake8 $(source_code)
+
+.PHONY: pydocstyle
+pydocstyle:
+ ve/bin/pydocstyle $(source_code)
+
+.PHONY: mypy
+mypy:
+ ve/bin/mypy $(source_code) --strict
+
+.PHONY: black-check
+black-check:
+ ve/bin/black -S $(source_code) --check
+
+.PHONY: isort-check
+isort-check:
+ $(isort) $(source_code) --diff --check
+
+.PHONY: lint
+lint: black-check isort-check
+
+.PHONY: test
+test:
+ ve/bin/python setup.py test
+
+.PHONY: coverage
+coverage:
+ ve/bin/coverage run --branch setup.py test
+ ve/bin/coverage xml # the XML output file is used by the "badges" target
+ PYTHONWARNINGS=ignore ve/bin/coverage report --ignore-errors --fail-under=97 --show-missing --skip-empty
+
+.PHONY: check
+check: test lint coverage
+
+########################################################################################
+# Sorce code formatting targets
+
+.PHONY: black
+black:
+ ve/bin/black -S $(source_code)
+
+.PHONY: isort
+isort:
+ $(isort) $(source_code)
+
+########################################################################################
+# Cleanup targets
+
+.PHONY: clean-%
+clean-%:
+ rm -rf $*
+
+.PHONY: clean-pycache
+clean-pycache:
+ find . -name __pycache__ -delete
+
+.PHONY: clean
+clean: clean-ve clean-pycache clean-dist
diff -Nru manuel-1.10.1/MANIFEST.in manuel-1.12.4/MANIFEST.in
--- manuel-1.10.1/MANIFEST.in 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/MANIFEST.in 2022-06-24 11:50:26.000000000 +0000
@@ -3,9 +3,7 @@
recursive-include docs *
recursive-include sphinx *.py
include *.rst
-include tox.ini
-include bootstrap.py
-include buildout.cfg
+include Makefile
+include constraints.txt
include *.yaml
include .coveragerc
-include .circleci/*
diff -Nru manuel-1.10.1/README.rst manuel-1.12.4/README.rst
--- manuel-1.10.1/README.rst 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/README.rst 2022-06-24 11:50:26.000000000 +0000
@@ -1,10 +1,4 @@
-.. image:: https://travis-ci.org/benji-york/manuel.png?branch=master
- :target: https://travis-ci.org/benji-york/manuel
-
-.. image:: https://coveralls.io/repos/github/benji-york/manuel/badge.svg?branch=master
- :target: https://coveralls.io/github/benji-york/manuel?branch=master
-
-.. image:: https://img.shields.io/pypi/v/manuel.svg
+.. image:: https://raw.githubusercontent.com/benji-york/manuel/master/badges/coverage-badge.svg
:target: https://pypi.python.org/pypi/manuel
.. image:: https://img.shields.io/pypi/pyversions/manuel.svg
@@ -14,3 +8,14 @@
``_.
Source code and issues are managed at https://github.com/benji-york/manuel.
+
+
+Development
+===========
+
+To work on Manuel, check out the code and then run `make` to build a development
+environment.
+
+To run the tests, run ``make test``. To run all checks, run ``make check``.
+
+See the `Makefile` for more useful targets.
diff -Nru manuel-1.10.1/setup.py manuel-1.12.4/setup.py
--- manuel-1.10.1/setup.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/setup.py 2022-06-24 11:50:26.000000000 +0000
@@ -24,7 +24,7 @@
setup(
name='manuel',
- version='1.10.1',
+ version='1.12.4',
url='http://pypi.python.org/pypi/manuel',
packages=find_packages('src'),
package_dir={'': 'src'},
@@ -33,13 +33,11 @@
author_email='benji@benjiyork.com',
description='Manuel lets you build tested documentation.',
classifiers=[
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
+ 'Programming Language :: Python :: 3.8',
+ 'Programming Language :: Python :: 3.9',
+ 'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'License :: OSI Approved :: Apache Software License',
diff -Nru manuel-1.10.1/sphinx/conf.py manuel-1.12.4/sphinx/conf.py
--- manuel-1.10.1/sphinx/conf.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/sphinx/conf.py 2022-06-24 11:50:26.000000000 +0000
@@ -1,4 +1,8 @@
-source_suffix = '.txt'
+source_suffix = {
+ '.rst': 'restructuredtext',
+ '.txt': 'restructuredtext',
+ '.md': 'markdown',
+}
master_doc = 'index'
project = 'Manuel'
copyright = 'Benji York'
@@ -13,3 +17,7 @@
todo_include_todos = False
exclude_dirnames = ['manuel.egg-info']
unused_docs = ['manuel/capture']
+extensions = [
+ "myst_parser",
+ "sphinx_copybutton",
+]
diff -Nru manuel-1.10.1/src/manuel/capture.py manuel-1.12.4/src/manuel/capture.py
--- manuel-1.10.1/src/manuel/capture.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/src/manuel/capture.py 2022-06-24 11:50:26.000000000 +0000
@@ -4,8 +4,8 @@
import textwrap
CAPTURE_DIRECTIVE = re.compile(
- r'^(?P(\t| )*)\.\.\s*->\s*(?P\S+).*$',
- re.MULTILINE)
+ r'^(?P(\t| )*)\.\.\s*->\s*(?P\S+).*$', re.MULTILINE
+)
class Capture(object):
@@ -13,8 +13,9 @@
self.name = name
self.block = block
+
def normalize_whitespace(s):
- return s.replace('\t', ' '*8) # turn tabs into spaces
+ return s.replace('\t', ' ' * 8) # turn tabs into spaces
@manuel.timing(manuel.EARLY)
@@ -58,8 +59,7 @@
lines = found_region.source.splitlines()
if found_region.lineno + len(lines) < end:
- raise RuntimeError('both start and end lines must be in the '
- 'same region')
+ raise RuntimeError('both start and end lines must be in the ' 'same region')
start = None
for offset, line in reversed(list(enumerate(lines))):
@@ -70,14 +70,17 @@
start = offset + 1
if start is None:
- raise RuntimeError("couldn't find the start of the block; "
- "improper indentation of capture directive?")
-
- _, temp_region = document.split_region(found_region,
- found_region.lineno+start)
+ raise RuntimeError(
+ "couldn't find the start of the block; "
+ "improper indentation of capture directive?"
+ )
+
+ _, temp_region = document.split_region(
+ found_region, found_region.lineno + start
+ )
# there are some extra lines in the new region, trim them off
- final_region, _ = document.split_region(temp_region, end+1)
+ final_region, _ = document.split_region(temp_region, end + 1)
document.remove_region(final_region)
name = region.start_match.group('name')
diff -Nru manuel-1.10.1/src/manuel/codeblock.py manuel-1.12.4/src/manuel/codeblock.py
--- manuel-1.10.1/src/manuel/codeblock.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/src/manuel/codeblock.py 2022-06-24 11:50:26.000000000 +0000
@@ -1,10 +1,11 @@
-import re
import manuel
+import re
import textwrap
CODEBLOCK_START = re.compile(
- r'(^\.\.\s*(invisible-)?code(-block)?::?\s*python\b(?:\s*\:[\w-]+\:.*\n)*)',
- re.MULTILINE)
+ r'(^\.\.\s*(invisible-)?code(-block)?::?\s*python\b(?:\s*\:[\w-]+\:.*\n)*)', # noqa
+ re.MULTILINE,
+)
CODEBLOCK_END = re.compile(r'(\n\Z|\n(?=\S))')
@@ -29,7 +30,7 @@
return
exec(region.parsed.code, globs)
- del globs['__builtins__'] # exec adds __builtins__, we don't want it
+ del globs['__builtins__'] # exec adds __builtins__, we don't want it
class Manuel(manuel.Manuel):
diff -Nru manuel-1.10.1/src/manuel/doctest.py manuel-1.12.4/src/manuel/doctest.py
--- manuel-1.10.1/src/manuel/doctest.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/src/manuel/doctest.py 2022-06-24 11:50:26.000000000 +0000
@@ -1,9 +1,9 @@
from __future__ import absolute_import
import doctest
-import six
import manuel
import os.path
+import six
DocTestRunner = doctest.DocTestRunner
DebugRunner = doctest.DebugRunner
@@ -25,8 +25,7 @@
continue
chunk._manual = m
- chunk_line_count = (chunk.source.count('\n')
- + chunk.want.count('\n'))
+ chunk_line_count = chunk.source.count('\n') + chunk.want.count('\n')
split_line_1 = region_start + chunk.lineno
split_line_2 = split_line_1 + chunk_line_count
@@ -53,8 +52,9 @@
class DocTest(doctest.DocTest):
def __init__(self, examples, globs, name, filename, lineno, docstring):
# do everything like regular doctests, but don't make a copy of globs
- doctest.DocTest.__init__(self, examples, globs, name, filename, lineno,
- docstring)
+ doctest.DocTest.__init__(
+ self, examples, globs, name, filename, lineno, docstring
+ )
self.globs = globs
@@ -77,16 +77,19 @@
# Use the testrunner-set option flags when running these tests.
old_optionflags = runner.optionflags
runner.optionflags |= doctest._unittest_reportflags
- runner.DIVIDER = '' # disable unwanted result formatting
+ runner.DIVIDER = '' # disable unwanted result formatting
# Here's where everything happens.
example = region.parsed
runner.run(
- DocTest([example], globs, test_name,
- document.location, region.lineno-1, None),
- out=out, clear_globs=False)
+ DocTest(
+ [example], globs, test_name, document.location, region.lineno - 1, None
+ ),
+ out=out,
+ clear_globs=False,
+ )
- runner.optionflags = old_optionflags # Reset the option flags.
+ runner.optionflags = old_optionflags # Reset the option flags.
region.evaluated = result
@@ -98,16 +101,20 @@
class Manuel(manuel.Manuel):
-
def __init__(self, optionflags=0, checker=None, parser=None):
- self.runner = DocTestRunner(optionflags=optionflags,
- checker=checker, verbose=False)
+ self.runner = DocTestRunner(
+ optionflags=optionflags, checker=checker, verbose=False
+ )
self.debug_runner = DebugRunner(optionflags=optionflags, verbose=False)
+
def evaluate_closure(region, document, globs):
# capture "self"
evaluate(self, region, document, globs)
+
parser = parser or doctest.DocTestParser()
manuel.Manuel.__init__(
self,
[lambda document: parse(self, document, parser)],
- [evaluate_closure], [format])
+ [evaluate_closure],
+ [format],
+ )
diff -Nru manuel-1.10.1/src/manuel/footnote.py manuel-1.12.4/src/manuel/footnote.py
--- manuel-1.10.1/src/manuel/footnote.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/src/manuel/footnote.py 2022-06-24 11:50:26.000000000 +0000
@@ -1,10 +1,9 @@
-import re
import manuel
+import re
FOOTNOTE_REFERENCE_LINE_RE = re.compile(r'^.*\[([^\]]+)]_.*$', re.MULTILINE)
FOOTNOTE_REFERENCE_RE = re.compile(r'\[([^\]]+)]_')
-FOOTNOTE_DEFINITION_RE = re.compile(
- r'^\.\.\s*\[\s*([^\]]+)\s*\].*$', re.MULTILINE)
+FOOTNOTE_DEFINITION_RE = re.compile(r'^\.\.\s*\[\s*([^\]]+)\s*\].*$', re.MULTILINE)
END_OF_FOOTNOTE_RE = re.compile(r'^\S.*$', re.MULTILINE)
@@ -43,8 +42,7 @@
@manuel.timing(manuel.LATE)
def do_footnotes(document):
- """Copy footnoted items into their appropriate position.
- """
+ """Copy footnoted items into their appropriate position."""
# first find all the regions that are in footnotes
footnotes = {}
name = None
diff -Nru manuel-1.10.1/src/manuel/ignore.py manuel-1.12.4/src/manuel/ignore.py
--- manuel-1.10.1/src/manuel/ignore.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/src/manuel/ignore.py 2022-06-24 11:50:26.000000000 +0000
@@ -1,5 +1,5 @@
-import re
import manuel
+import re
import textwrap
IGNORE_START = re.compile(r'^\.\.\s*ignore-next-block\s*$', re.MULTILINE)
@@ -7,6 +7,7 @@
baseline = {}
+
def find_ignores(document):
for region in document.find_regions(IGNORE_START, IGNORE_END):
document.claim_region(region)
diff -Nru manuel-1.10.1/src/manuel/index.txt manuel-1.12.4/src/manuel/index.txt
--- manuel-1.10.1/src/manuel/index.txt 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/src/manuel/index.txt 2022-06-24 11:50:26.000000000 +0000
@@ -779,7 +779,7 @@
File ":4", line 2
def foo:
^
- SyntaxError: invalid syntax...
+ SyntaxError: ...
The :mod:`manuel.ignore` module provides a way to ignore parts of a document
using a directive ".. ignore-next-block".
@@ -1146,3 +1146,4 @@
README.txt
table-example.txt
bugs.txt
+ myst-markdown.md
diff -Nru manuel-1.10.1/src/manuel/__init__.py manuel-1.12.4/src/manuel/__init__.py
--- manuel-1.10.1/src/manuel/__init__.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/src/manuel/__init__.py 2022-06-24 11:50:26.000000000 +0000
@@ -4,8 +4,10 @@
EARLY = 'early'
LATE = 'late'
+
def timing(timing):
assert timing in (EARLY, LATE)
+
def decorate(func):
func.manuel_timing = timing
return func
@@ -26,8 +28,9 @@
evaluated = None
formatted = None
- def __init__(self, lineno, source, start_match=None, end_match=None,
- provenance=None):
+ def __init__(
+ self, lineno, source, start_match=None, end_match=None, provenance=None
+ ):
self.lineno = lineno
self.source = newlineify(source)
self.start_match = start_match
@@ -35,8 +38,7 @@
self.provenance = provenance
def copy(self):
- """Private utility function to make a copy of this region.
- """
+ """Private utility function to make a copy of this region."""
copy = Region(self.lineno, self.source, provenance=self.provenance)
copy.parsed = self.parsed
copy.evaluated = self.evaluated
@@ -49,17 +51,13 @@
def check_region_start(region, match):
- if match.start() != 0 \
- and region.source[match.start()-1] != '\n':
- raise ValueError(
- 'Regions must start at the begining of a line.')
+ if match.start() != 0 and region.source[match.start() - 1] != '\n':
+ raise ValueError('Regions must start at the begining of a line.')
def check_region_end(region, match):
- if match.end() != len(region.source) \
- and region.source[match.end()-1] != '\n':
- raise ValueError(
- 'Regions must end at the ending of a line.')
+ if match.end() != len(region.source) and region.source[match.end() - 1] != '\n':
+ raise ValueError('Regions must end at the ending of a line.')
def lines_to_string(lines):
@@ -80,10 +78,9 @@
new_regions = []
# figure out if there are any lines before the given region
- before_lines = lines[:new.lineno-original.lineno]
+ before_lines = lines[: new.lineno - original.lineno]
if before_lines:
- new_regions.append(
- Region(original.lineno, lines_to_string(before_lines)))
+ new_regions.append(Region(original.lineno, lines_to_string(before_lines)))
# put in the parsed
new_regions.append(new)
@@ -91,14 +88,12 @@
# figure out if there are any lines after the given region
assert new.source[-1] == '\n', 'all lines must end with a newline'
lines_in_new = new.source.count('\n')
- after_lines = lines[len(before_lines)+lines_in_new:]
+ after_lines = lines[len(before_lines) + lines_in_new :]
if after_lines:
first_line_after_new = new.lineno + lines_in_new
- new_regions.append(
- Region(first_line_after_new, lines_to_string(after_lines)))
+ new_regions.append(Region(first_line_after_new, lines_to_string(after_lines)))
- assert original.source.count('\n') == \
- sum(r.source.count('\n') for r in new_regions)
+ assert original.source.count('\n') == sum(r.source.count('\n') for r in new_regions)
return new_regions
@@ -139,23 +134,20 @@
formatter(self)
def process_with(self, m, globs):
- """Run all phases of document processing using a Manuel instance.
- """
+ """Run all phases of document processing using a Manuel instance."""
self.parse_with(m)
self.evaluate_with(m, globs)
self.format_with(m)
def formatted(self):
- """Return a string of all non-boolean-false formatted regions.
- """
+ """Return a string of all non-boolean-false formatted regions."""
return ''.join(region.formatted for region in self if region.formatted)
def append(self, region):
self.regions.append(region)
def __iter__(self):
- """Iterate over all regions of the document.
- """
+ """Iterate over all regions of the document."""
return iter(self.regions)
def __bool__(self):
@@ -163,7 +155,6 @@
class Document(RegionContainer):
-
def __init__(self, source, location=None):
RegionContainer.__init__(self)
@@ -191,8 +182,9 @@
continue
for start_match in re.finditer(start, region.source):
- first_lineno = region.lineno + find_line(
- region.source, start_match.start()) - 1
+ first_lineno = (
+ region.lineno + find_line(region.source, start_match.start()) - 1
+ )
check_region_start(region, start_match)
if end is None:
@@ -205,9 +197,10 @@
# couldn't find a match for the end re, try again
if end_match is None:
continue
- end_position = end_match.end() + \
- find_end_of_line(region.source[end_match.end():])
- text = region.source[start_match.start():end_position]
+ end_position = end_match.end() + find_end_of_line(
+ region.source[end_match.end() :]
+ )
+ text = region.source[start_match.start() : end_position]
if text[-1] != '\n':
text += '\n'
@@ -230,12 +223,13 @@
del self.regions[region_index]
lines_in_source1 = source1.count('\n')
region1 = Region(region.lineno, source1)
- region2 = Region(region.lineno+lines_in_source1, source2)
+ region2 = Region(region.lineno + lines_in_source1, source2)
self.regions.insert(region_index, region2)
self.regions.insert(region_index, region1)
if not region.source == source1 + source2:
- raise RuntimeError('when splitting a region, combined results do '
- 'not equal the input')
+ raise RuntimeError(
+ 'when splitting a region, combined results do ' 'not equal the input'
+ )
return region1, region2
def claim_region(self, to_be_replaced):
@@ -247,12 +241,11 @@
assert not region.parsed
new_regions.extend(break_up_region(region, to_be_replaced))
break
- elif region.lineno > to_be_replaced.lineno: # we "overshot"
+ elif region.lineno > to_be_replaced.lineno: # we "overshot"
assert not new_regions[-1].parsed
to_be_broken = new_regions[-1]
del new_regions[-1]
- new_regions.extend(break_up_region(
- to_be_broken, to_be_replaced))
+ new_regions.extend(break_up_region(to_be_broken, to_be_replaced))
new_regions.append(region)
break
@@ -270,11 +263,13 @@
def insert_region(self, where, marker_region, new_region):
if new_region in self.regions:
raise ValueError(
- 'Only regions not already in the document may be inserted.')
+ 'Only regions not already in the document may be inserted.'
+ )
if new_region in self.shadow_regions:
raise ValueError(
'Regions returned by "find_regions" can not be directly '
- 'inserted into a document. Use "claim_region" instead.')
+ 'inserted into a document. Use "claim_region" instead.'
+ )
for index, region in enumerate(self.regions):
if region is marker_region:
diff -Nru manuel-1.10.1/src/manuel/isolation.py manuel-1.12.4/src/manuel/isolation.py
--- manuel-1.10.1/src/manuel/isolation.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/src/manuel/isolation.py 2022-06-24 11:50:26.000000000 +0000
@@ -1,5 +1,5 @@
-import re
import manuel
+import re
import textwrap
RESET = re.compile(r'^\.\.\s*reset-globs\s*$', re.MULTILINE)
@@ -7,6 +7,7 @@
baseline = {}
+
class Reset(object):
pass
@@ -48,5 +49,6 @@
class Manuel(manuel.Manuel):
def __init__(self):
- manuel.Manuel.__init__(self, [find_reset, find_baseline],
- [execute_reset, execute_baseline])
+ manuel.Manuel.__init__(
+ self, [find_reset, find_baseline], [execute_reset, execute_baseline]
+ )
diff -Nru manuel-1.10.1/src/manuel/myst/codeblock.py manuel-1.12.4/src/manuel/myst/codeblock.py
--- manuel-1.10.1/src/manuel/myst/codeblock.py 1970-01-01 00:00:00.000000000 +0000
+++ manuel-1.12.4/src/manuel/myst/codeblock.py 2022-06-24 11:50:26.000000000 +0000
@@ -0,0 +1,27 @@
+import re
+import textwrap
+from manuel import Manuel as BaseManuel
+from manuel.codeblock import CodeBlock, execute_code_block
+
+CODEBLOCK_START = re.compile(
+ r"((^```python)|(^% invisible-code-block:\s+python)$)",
+ re.MULTILINE,
+)
+CODEBLOCK_END = re.compile(r"(\n(?=```\n))|((?:% [\S ]*)\n(?=\n))")
+
+
+def find_code_blocks(document):
+ for region in document.find_regions(CODEBLOCK_START, CODEBLOCK_END):
+ start_end = CODEBLOCK_START.search(region.source).end()
+ source = textwrap.dedent(region.source[start_end:])
+ # MyST comments
+ source = re.sub(r'\n%[ ]?', '\n', source)
+ source_location = "%s:%d" % (document.location, region.lineno)
+ code = compile(source, source_location, "exec", 0, True)
+ document.claim_region(region)
+ region.parsed = CodeBlock(code, source)
+
+
+class Manuel(BaseManuel):
+ def __init__(self):
+ BaseManuel.__init__(self, [find_code_blocks], [execute_code_block])
diff -Nru manuel-1.10.1/src/manuel/myst-markdown.md manuel-1.12.4/src/manuel/myst-markdown.md
--- manuel-1.10.1/src/manuel/myst-markdown.md 1970-01-01 00:00:00.000000000 +0000
+++ manuel-1.12.4/src/manuel/myst-markdown.md 2022-06-24 11:50:26.000000000 +0000
@@ -0,0 +1,79 @@
+# MyST markdown
+
+Manuel was originally written for reStructuredText.
+Starting with the {mod}`manuel.codeblock` module, Manuel will successively be extended for MyST, a Markdown flavor.
+[Read about `MyST`](https://myst-parser.readthedocs.io/en/latest/).
+
+## Code Blocks
+
+Sphinx and other docutils extensions provide a `code-block` directive, which allows inlined snippets of code in MyST documents.
+
+Several plug-ins are included that provide new test syntax (see
+{ref}`functionality`).
+You can also create your own plug-ins.
+
+For example, if you've ever wanted to include a large chunk of Python in a
+doctest but were irritated by all the `>>>` and `...` prompts required, you'd
+like the {mod}`manuel.myst.codeblock` module.
+It lets you execute code using MyST-style code block directives containing unpolluted Python code.
+The markup looks like this:
+
+ ```python
+ import foo
+
+ def my_func(bar):
+ return foo.baz(bar)
+ ```
+
+
+To get Manuel wired up, see {ref}`getting-started`.
+To run doctests in MyST, use {mod}`manuel.myst.codeblock`.
+
+The scope of variables spans across the complete document.
+
+```python
+a = 3
+
+# another variable
+b = 2 * 3
+```
+
+The variables `a` and `b` can be used in the subsequent code block.
+
+```python
+assert b == 6
+```
+
+For better test feedback, you can use the methods of [`unittest.TestCase`](https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertEqual).
+
+```python
+self.assertEqual(b, 6)
+```
+
+The output of `self.assertEqual(b, 9999)` would be the following.
+
+```console
+AssertionError: 6 != 9999
+```
+
+You can even write code in invisible code blocks.
+Invisible code blocks do not show up in the rendered documentation.
+Using MyST syntax, lines that start with `%` are comments.
+The markup looks like this:
+
+ % invisible-code-block: python
+ %
+ % self.assertEqual(a+b, 9)
+ %
+ % self.assertEqual(7 * a, 21)
+
+
+% invisible-code-block: python
+%
+% self.assertEqual(a+b, 9)
+%
+% self.assertEqual(7 * a, 21)
+
+Invisible code blocks are tested like normal code blocks.
+
+Happy hacking!
diff -Nru manuel-1.10.1/src/manuel/testcase.py manuel-1.12.4/src/manuel/testcase.py
--- manuel-1.10.1/src/manuel/testcase.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/src/manuel/testcase.py 2022-06-24 11:50:26.000000000 +0000
@@ -9,6 +9,7 @@
SECTION_UNDERLINE = re.compile('^[' + punctuation + ']+\s*$', re.MULTILINE)
MARKER = re.compile(r'^.. test-case: (\S+)', re.MULTILINE)
+
def find_section_headers(document):
for region in document.find_regions(SECTION_TITLE, SECTION_UNDERLINE):
# regions that represent titles will have two lines
diff -Nru manuel-1.10.1/src/manuel/testing.py manuel-1.12.4/src/manuel/testing.py
--- manuel-1.10.1/src/manuel/testing.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/src/manuel/testing.py 2022-06-24 11:50:26.000000000 +0000
@@ -3,26 +3,24 @@
import doctest as real_doctest
import functools
import inspect
+import io
import itertools
import manuel
-import io
import os.path
import re
import sys
import types
import unittest
-
__all__ = ['TestSuite', 'TestFactory']
-class TestCaseMarker(object):
+class TestCaseMarker(object):
def __init__(self, id=''):
self.id = id
class TestCase(unittest.TestCase):
-
def __init__(self, m, regions, globs, setUp=None, tearDown=None):
unittest.TestCase.__init__(self)
self.manuel = m
@@ -44,9 +42,10 @@
self.regions.format_with(self.manuel)
results = [r.formatted for r in self.regions if r.formatted]
if results:
- DIVIDER = '-'*70 + '\n'
+ DIVIDER = '-' * 70 + '\n'
raise real_doctest.DocTestCase.failureException(
- '\n' + DIVIDER + DIVIDER.join(results))
+ '\n' + DIVIDER + DIVIDER.join(results)
+ )
def debug(self):
self.setUp()
@@ -73,7 +72,7 @@
while True:
accumulated_regions = manuel.RegionContainer()
while True:
- region = None # being defensive
+ region = None # being defensive
try:
region = next(document_iter)
except StopIteration:
@@ -106,6 +105,7 @@
# put the region we peeked at back so the inner loop can consume it
document_iter = itertools.chain([region], document_iter)
+
# copied from zope.testing.doctest
def _module_relative_path(module, path):
if not inspect.ismodule(module):
@@ -119,14 +119,17 @@
basedir = os.path.split(module.__file__)[0]
elif module.__name__ == '__main__':
# An interactive session.
- if len(sys.argv)>0 and sys.argv[0] != '':
+ if len(sys.argv) > 0 and sys.argv[0] != '':
basedir = os.path.split(sys.argv[0])[0]
else:
basedir = os.curdir
else:
# A module w/o __file__ (this includes builtins)
- raise ValueError("Can't resolve paths relative to the module " +
- module + " (it has no __file__)")
+ raise ValueError(
+ "Can't resolve paths relative to the module "
+ + module
+ + " (it has no __file__)"
+ )
# Combine the base directory and the path.
return os.path.join(basedir, *(path.split('/')))
@@ -166,8 +169,7 @@
# walk up the stack frame to find the module that called this function
for depth in range(1, 5):
try:
- calling_module = \
- sys.modules[sys._getframe(depth).f_globals['__name__']]
+ calling_module = sys.modules[sys._getframe(depth).f_globals['__name__']]
except KeyError:
continue
else:
@@ -177,34 +179,33 @@
if os.path.isabs(path):
abs_path = os.path.normpath(path)
else:
- abs_path = \
- os.path.abspath(_module_relative_path(calling_module, path))
+ abs_path = os.path.abspath(_module_relative_path(calling_module, path))
with io.open(abs_path, 'rt', newline=None) as fp:
contents = fp.read()
if not isinstance(contents, str):
# Python 2, we read unicode, but we really need a str
- contents = str.encode("utf-8")
- document = manuel.Document(
- contents, location=abs_path)
+ contents = contents.encode("utf-8")
+ document = manuel.Document(contents, location=abs_path)
document.parse_with(m)
for regions in group_regions_by_test_case(document):
- suite.addTest(TestCase_class(m, regions, globs, **kws))
+ tc = TestCase_class(m, regions, globs, **kws)
+ tc.globs['self'] = tc
+ suite.addTest(tc)
return suite
_not_word = re.compile(r'\W')
-class TestFactory:
+
+class TestFactory:
def __init__(self, m):
self.m = m
def __call__(self, path):
- base = os.path.dirname(os.path.abspath(
- sys._getframe(2).f_globals['__file__']
- ))
+ base = os.path.dirname(os.path.abspath(sys._getframe(2).f_globals['__file__']))
path = os.path.join(base, path)
with open(path) as f:
test = f.read()
diff -Nru manuel-1.10.1/src/manuel/tests.py manuel-1.12.4/src/manuel/tests.py
--- manuel-1.10.1/src/manuel/tests.py 2018-11-15 14:22:26.000000000 +0000
+++ manuel-1.12.4/src/manuel/tests.py 2022-06-24 11:50:26.000000000 +0000
@@ -6,6 +6,7 @@
import manuel.codeblock
import manuel.doctest
import manuel.ignore
+import manuel.myst.codeblock
import manuel.testcase
import manuel.testing
import os.path
@@ -15,20 +16,31 @@
here = os.path.dirname(os.path.abspath(__file__))
-checker = zope.testing.renormalizing.RENormalizing([
- (re.compile(r"