[med-svn] [Git][med-team/sphinxcontrib-autoprogram][master] 13 commits: New upstream version 0.1.7rc1
Andreas Tille (@tille)
gitlab at salsa.debian.org
Thu Oct 7 11:09:32 BST 2021
Andreas Tille pushed to branch master at Debian Med / sphinxcontrib-autoprogram
Commits:
11016de6 by Andreas Tille at 2021-10-07T11:58:31+02:00
New upstream version 0.1.7rc1
- - - - -
9708b3ed by Andreas Tille at 2021-10-07T12:03:33+02:00
Fix watchfile to detect new versions on github
- - - - -
70debd2d by Andreas Tille at 2021-10-07T12:03:55+02:00
Drop debian/.gitignore
- - - - -
9a1eb64d by Andreas Tille at 2021-10-07T12:04:40+02:00
Remove Tim Booth from Uploaders (Thanks Tim for all your work)
- - - - -
ab171e21 by Andreas Tille at 2021-10-07T12:04:55+02:00
New upstream version 0.1.7
- - - - -
101a668f by Andreas Tille at 2021-10-07T12:04:55+02:00
routine-update: New upstream version
- - - - -
5ac9ef70 by Andreas Tille at 2021-10-07T12:04:55+02:00
Update upstream source from tag 'upstream/0.1.7'
Update to upstream version '0.1.7'
with Debian dir eb11f5e60a11eaf5b42b0c71ec5a32a1b0f6c21f
- - - - -
cfd7751a by Andreas Tille at 2021-10-07T12:04:56+02:00
routine-update: Standards-Version: 4.6.0
- - - - -
809ebab1 by Andreas Tille at 2021-10-07T12:04:56+02:00
routine-update: debhelper-compat 13
- - - - -
4de64dfe by Andreas Tille at 2021-10-07T12:05:01+02:00
routine-update: Add salsa-ci file
- - - - -
5855e3b0 by Andreas Tille at 2021-10-07T12:05:01+02:00
routine-update: Rules-Requires-Root: no
- - - - -
459e05ab by Andreas Tille at 2021-10-07T12:05:06+02:00
Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, Repository-Browse.
Changes-By: lintian-brush
Fixes: lintian: upstream-metadata-file-is-missing
See-also: https://lintian.debian.org/tags/upstream-metadata-file-is-missing.html
Fixes: lintian: upstream-metadata-missing-bug-tracking
See-also: https://lintian.debian.org/tags/upstream-metadata-missing-bug-tracking.html
Fixes: lintian: upstream-metadata-missing-repository
See-also: https://lintian.debian.org/tags/upstream-metadata-missing-repository.html
- - - - -
46e643de by Andreas Tille at 2021-10-07T12:06:03+02:00
routine-update: Ready to upload to unstable
- - - - -
21 changed files:
- + .github/workflows/build.yml
- .gitignore
- + .pylintrc
- − .travis.yml
- + LICENSE
- MANIFEST.in
- README.rst
- − debian/.gitignore
- debian/changelog
- debian/control
- + debian/salsa-ci.yml
- + debian/upstream/metadata
- debian/watch
- doc/changelog.rst
- doc/conf.py
- doc/index.rst
- + mypy.ini
- setup.py
- sphinxcontrib/__init__.py
- sphinxcontrib/autoprogram.py
- tox.ini
Changes:
=====================================
.github/workflows/build.yml
=====================================
@@ -0,0 +1,77 @@
+name: Build and Test
+
+on:
+ release:
+ types: [ published ]
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python-version: [3.7, 3.8, 3.9]
+
+ steps:
+ - uses: actions/checkout at v2
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python at v2
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ python -m pip install tox
+
+ # NOTE(lb): By creating a sdist and then testing/working with that, we ensure
+ # its completeness.
+ - name: Move to sdist
+ run: |
+ python setup.py sdist
+ mkdir tmp
+ mv dist/*.tar.gz tmp/
+ cd tmp/
+ tar xvf *.tar.gz
+ for f in *; do
+ if [[ -d ${f} ]]; then
+ printf "Moving %s to %s\n" "${f}" "$PWD/sdist"
+ mv "${f}" sdist
+ break
+ fi
+ done
+
+ - name: Build
+ run: |
+ pwd
+ pip install wheel
+ python setup.py sdist bdist_wheel
+ working-directory: ${{ github.workspace }}/tmp/sdist
+
+ - name: Tox
+ run: |
+ tox --skip-missing-interpreters
+ working-directory: ${{ github.workspace }}/tmp/sdist
+
+ - name: Build documentation
+ run: |
+ pip install -r doc/rtd-requires.txt
+ cd doc
+ make
+ working-directory: ${{ github.workspace }}/tmp/sdist
+
+ - name: Install wheel
+ run: |
+ pip install dist/*.whl
+ working-directory: ${{ github.workspace }}/tmp/sdist
+
+ - name: Publish package
+ if: github.event_name == 'release' && startsWith(github.ref, 'refs/tags') && matrix.python-version == 3.9
+ uses: pypa/gh-action-pypi-publish at v1.4.2
+ with:
+ user: __token__
+ password: ${{ secrets.PYPI_API_TOKEN }}
+ packages_dir: ${{ github.workspace }}/tmp/sdist/dist
=====================================
.gitignore
=====================================
@@ -1,7 +1,10 @@
*.egg-info/
+mypy_cache/
*.pyc
.*.swp
.cache/
.tox/
__pycache__/
+build
+dist
doc/_build/
=====================================
.pylintrc
=====================================
@@ -0,0 +1,55 @@
+[MASTER]
+
+ignore=.git
+jobs=0
+suggestion-mode=yes
+
+[MESSAGES CONTROL]
+
+disable=all
+enable=assign-to-new-keyword,
+ bad-format-character,
+ bad-format-string-key,
+ bad-open-mode,
+ bad-string-format-type,
+ consider-using-get,
+ consider-using-join,
+ empty-docstring,
+ function-redefined,
+ not-in-loop,
+ redefined-builtin,
+ return-in-init,
+ too-few-format-args,
+ too-many-format-args,
+ truncated-format-string,
+ unnecessary-comprehension,
+ unused-argument,
+ unreachable,
+ unused-format-string-argument,
+ unused-format-string-key,
+ unused-import,
+ unused-variable,
+ using-constant-test
+
+[REPORTS]
+
+output-format=text
+reports=no
+score=no
+
+[BASIC]
+
+argument-naming-style=snake_case
+attr-naming-style=snake_case
+class-naming-style=PascalCase
+const-naming-style=UPPER_CASE
+function-naming-style=snake_case
+include-naming-hint=yes
+inlinevar-naming-style=snake_case
+method-naming-style=snake_case
+module-naming-style=snake_case
+variable-naming-style=snake_case
+
+[VARIABLES]
+
+redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
\ No newline at end of file
=====================================
.travis.yml deleted
=====================================
@@ -1,26 +0,0 @@
-language: python
-python: [3.6, 3.5, 3.4, 3.3, pypy, 2.7, 2.6]
-install:
-- pip install tox-travis
-script:
-- tox
-- '[[ "$TRAVIS_TAG" = "" ]] || [[ "$TRAVIS_TAG" = "$(python setup.py --version)" ]]'
-- |
- if git show --format=%B --quiet "${TRAVIS_PULL_REQUEST_SHA:-${TRAVIS_TAG:-${TRAVIS_COMMIT}}}" \
- | grep '\[changelog skip\]' > /dev/null; then
- echo "Skip changelog checker..."
- elif [[ "$TRAVIS_TAG" != "" ]]; then
- ! grep -i "to be released" CHANGES.rst
- else
- [[ "$TRAVIS_COMMIT_RANGE" = "" ]] || \
- [[ "$(git diff --name-only "$TRAVIS_COMMIT_RANGE" | grep doc/changelog\.rst)" != "" ]]
- fi
-deploy:
- provider: pypi
- distributions: 'sdist bdist_wheel'
- on:
- tags: true
- python: 3.6
- user: sphinxcontrib-httpdomain # shared with sphinx-contrib/httpdomain
- password:
- secure: "a80qMBk4Z7ROd9A9BQnovTL7iBLbPp79LuNjPA68ZCgt6BHVlGcRjEzVzNeTBBEZqrBr8tMiu55CdgXj8jscFCs5MEoJJVeE/21JM6kFpLoyq3qR5moHelzj/Mm12vn2KpuRo9YjJkTZ8SSPV3yVaEhTXlpwW64C1kqotjfEUtd4Vb90TAOxRTUVHMJ8g77mGWWOI8vYyZEJBOBx1nUTasTOQOlv/yDrzQJlMTjEAbyG0YinryhOOnXTXhlb3X9dHN9LMJv0zrKLwT99SrAbKgnGFHkII0JCe8v7O2UITbfpU/tljyijzVMqjjkCzvQVQlwJWTrhSv8eqBNvZ0kchhECF7u6hfpgXUd8t96HOHEkqtdX1boxWnTVcb0+yAtoM72o3BTafumfv6dObcTAy9lwZoZOuCD3rtL5nzlQke8AfLcInfe45QZOvd+FGFSQhjDn1zkQJDBpx8hL+4WdhZ0F9sktf54q32IhoUvibUeb0BlJxtbuGDGOGnfbZLMtM0c4ok6egeYfI2+a1Q2agtzcEq+z0M9msat4FG43kSLdM2FGblSPQWYcX8trCL9kCVJFM+TMuqHtDQ2nXAeLFpjH6P1nt2U2uwgvlh75GM38DjpdH++nrdTQhlz8Tuf1X4TEWI9r4P9kk3wgYRmizqfrA9YkwP8seQaXQX6vZR0="
=====================================
LICENSE
=====================================
@@ -0,0 +1,26 @@
+Copyright (c) 2014–2018 Hong Minhee <https://hongminhee.org/>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS 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 OWNER OR CONTRIBUTORS 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.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of the sphinxcontrib-autoprogram project.
=====================================
MANIFEST.in
=====================================
@@ -1,3 +1,5 @@
include README
include LICENSE
include CHANGES.*
+tox.ini
+recursive-include doc *
=====================================
README.rst
=====================================
@@ -9,12 +9,12 @@
:target: https://sphinxcontrib-autoprogram.readthedocs.io/
:alt: Documentation Status
-.. image:: https://travis-ci.org/sphinx-contrib/autoprogram.svg?branch=master
+.. image:: https://github.com/sphinx-contrib/autoprogram/workflows/Build%20and%20Test/badge.svg?branch=master
+ :target: https://github.com/sphinx-contrib/autoprogram/actions
:alt: Build Status
- :target: https://travis-ci.org/sphinx-contrib/autoprogram
This contrib extension, ``sphinxcontrib.autoprogram``, provides an automated
-way to document CLI programs. It scans ``arparser.ArgumentParser`` object,
+way to document CLI programs. It scans ``argparse.ArgumentParser`` object,
and then expands it into a set of ``.. program::`` and ``.. option::``
directives.
=====================================
debian/.gitignore deleted
=====================================
@@ -1,10 +0,0 @@
-*.pyc
-*.swp
-
-*.log
-*.debhelper
-*.substvars
-
-files
-python3-sphinxcontrib.autoprogram/
-sphinxcontrib-autoprogram/
=====================================
debian/changelog
=====================================
@@ -1,3 +1,18 @@
+sphinxcontrib-autoprogram (0.1.7-1) unstable; urgency=medium
+
+ * Fix watchfile to detect new versions on github
+ * Drop debian/.gitignore
+ * Remove Tim Booth from Uploaders (Thanks Tim for all your work)
+ * New upstream version
+ * Standards-Version: 4.6.0 (routine-update)
+ * debhelper-compat 13 (routine-update)
+ * Add salsa-ci file (routine-update)
+ * Rules-Requires-Root: no (routine-update)
+ * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository,
+ Repository-Browse.
+
+ -- Andreas Tille <tille at debian.org> Thu, 07 Oct 2021 12:05:10 +0200
+
sphinxcontrib-autoprogram (0.1.5-2) unstable; urgency=medium
* Drop Python2 support
=====================================
debian/control
=====================================
@@ -1,21 +1,21 @@
Source: sphinxcontrib-autoprogram
Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Tim Booth <tbooth at ceh.ac.uk>,
- Andreas Tille <tille at debian.org>,
+Uploaders: Andreas Tille <tille at debian.org>,
Kevin Murray <spam at kdmurray.id.au>
Section: python
Testsuite: autopkgtest-pkg-python
Priority: optional
-Build-Depends: debhelper-compat (= 12),
+Build-Depends: debhelper-compat (= 13),
dh-python,
python3,
python3-setuptools,
python3-sphinx,
python3-six
-Standards-Version: 4.4.0
+Standards-Version: 4.6.0
Vcs-Browser: https://salsa.debian.org/med-team/sphinxcontrib-autoprogram
Vcs-Git: https://salsa.debian.org/med-team/sphinxcontrib-autoprogram.git
Homepage: https://pythonhosted.org/sphinxcontrib-autoprogram/
+Rules-Requires-Root: no
Package: python3-sphinxcontrib.autoprogram
Architecture: all
=====================================
debian/salsa-ci.yml
=====================================
@@ -0,0 +1,4 @@
+---
+include:
+ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
+ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
=====================================
debian/upstream/metadata
=====================================
@@ -0,0 +1,5 @@
+---
+Bug-Database: https://github.com/sphinx-contrib/autoprogram/issues
+Bug-Submit: https://github.com/sphinx-contrib/autoprogram/issues/new
+Repository: https://github.com/sphinx-contrib/autoprogram.git
+Repository-Browse: https://github.com/sphinx-contrib/autoprogram
=====================================
debian/watch
=====================================
@@ -1,3 +1,4 @@
version=4
-https://github.com/sphinx-contrib/autoprogram/releases .*/archive/@ANY_VERSION@@ARCHIVE_EXT@
+opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
+ https://github.com/sphinx-contrib/autoprogram/releases .*/@ANY_VERSION@@ARCHIVE_EXT@
=====================================
doc/changelog.rst
=====================================
@@ -1,6 +1,37 @@
Changelog
=========
+..
+ TODO Uncomment this:
+
+ Version 0.1.8
+ -------------
+
+ To be released.
+
+Version 0.1.7
+-------------
+
+Released on February 10, 2021.
+
+- Publish to PyPI via Github Actions.
+
+
+Version 0.1.6
+-------------
+
+Released on February 10, 2021.
+
+- Dropped support for Python 2 and Pypy
+- Declare this extension safe for parallel reading
+- Migrate to Github Actions for CI [:issue:`28`, :pull:`32` by Langston Barrett]
+- Test against recent versions of Sphinx [:issue:`33`, :pull:`32` by Langston Barrett]
+- Format source code with Black [:issue:`30`, :pull:`32` by Langston Barrett]
+- Add documentation to the ``sdist`` [:issue:`26`, :pull:`32` by Langston Barrett]
+- Fixed unwanted ``<blockquote>`` tags in multi-line command descriptions that
+ are indented to match surrounding code. [:pull:`21` by dgw]
+
+
Version 0.1.5
-------------
@@ -18,7 +49,7 @@ Released on February 27, 2018.
during build without ``:no_usage_codeblock:`` option on Python 2.
[:bbissue:`168`, :bbissue:`169`]
- Fixed an issue with Sphinx 1.7 which removed ``sphinx.util.compat``.
- [:issue:`1`, :issue:`2` by Zach Riggle]
+ [:issue:`1`, :pull:`2` by Zach Riggle]
Version 0.1.3
@@ -34,7 +65,7 @@ Released on October 7, 2016.
the top-level command usage). [:bbpull:`112` by Alex Honeywell]
- Added :ref:`new options <autoprogram-options>` to :rst:dir:`.. autoprogram::`
directive: [:bbpull:`112` by Alex Honeywell]
-
+
- ``maxdepth``
- ``no_usage_codeblock``
- ``start_command``
=====================================
doc/conf.py
=====================================
@@ -280,6 +280,10 @@ intersphinx_mapping = {
extlinks = {
+ 'pull': (
+ 'https://github.com/sphinx-contrib/autoprogram/pull/%s',
+ '#'
+ ),
'issue': (
'https://github.com/sphinx-contrib/autoprogram/issues/%s',
'#'
=====================================
doc/index.rst
=====================================
@@ -16,9 +16,9 @@
:target: https://sphinxcontrib-autoprogram.readthedocs.io/
:alt: Documentation Status
-.. image:: https://travis-ci.org/sphinx-contrib/autoprogram.svg?branch=master
+.. image:: https://github.com/sphinx-contrib/autoprogram/workflows/Build%20and%20Test/badge.svg?branch=master
+ :target: https://github.com/sphinx-contrib/autoprogram/actions
:alt: Build Status
- :target: https://travis-ci.org/sphinx-contrib/autoprogram
This contrib extension, :mod:`sphinxcontrib.autoprogram`, provides an automated
way to document CLI programs. It scans :class:`argparse.ArgumentParser`
@@ -144,7 +144,7 @@ Additional Options for :rst:dir:`.. autoprogram::`
``:start_command: subcommand``
Render document for the given subcommand. ``subcommand`` can be a space
- separated list to render a sub-sub-...-command.
+ separated list to render a sub-sub-...-command.
.. versionadded:: 0.1.3
=====================================
mypy.ini
=====================================
@@ -0,0 +1,11 @@
+[mypy]
+# TODO: Enable these
+# disallow_any_generics = True
+# disallow_incomplete_defs = True
+# disallow_untyped_defs = True
+show_error_codes = True
+ignore_missing_imports = True
+no_implicit_optional = True
+warn_redundant_casts = True
+warn_unused_configs = True
+warn_unused_ignores = True
\ No newline at end of file
=====================================
setup.py
=====================================
@@ -7,13 +7,10 @@ from setuptools import setup, find_packages
# Do not change the variable name. It's parsed by doc/conf.py script.
-version = '0.1.5'
+version = '0.1.7'
requires = ['Sphinx >= 1.2', 'six']
-if 'bdist_wheel' not in sys.argv and sys.version_info < (2, 7):
- requires.append('argparse')
-
def readme():
with open('README.rst') as f:
@@ -24,7 +21,7 @@ setup(
name='sphinxcontrib-autoprogram',
version=version,
url='https://github.com/sphinx-contrib/autoprogram',
- license='BSD',
+ license='2-Clause BSD',
author='Hong Minhee',
author_email='\x68\x6f\x6e\x67.minhee' '@' '\x67\x6d\x61\x69\x6c.com',
description='Documenting CLI programs',
@@ -38,14 +35,15 @@ setup(
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
- 'Programming Language :: Python :: 2.6',
- 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.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',
'Programming Language :: Python :: Implementation :: Stackless',
'Topic :: Documentation',
'Topic :: Software Development :: Documentation',
@@ -56,6 +54,5 @@ setup(
namespace_packages=['sphinxcontrib'],
include_package_data=True,
install_requires=requires,
- extras_requires={":python_version=='2.6'": ['argparse']},
test_suite='sphinxcontrib.autoprogram.suite'
)
=====================================
sphinxcontrib/__init__.py
=====================================
@@ -10,4 +10,4 @@
:license: BSD, see LICENSE for details.
"""
-__import__('pkg_resources').declare_namespace(__name__)
+__import__("pkg_resources").declare_namespace(__name__)
=====================================
sphinxcontrib/autoprogram.py
=====================================
@@ -8,12 +8,16 @@
:license: BSD, see LICENSE for details.
"""
+from __future__ import annotations
+
# pylint: disable=protected-access,missing-docstring
import argparse
import collections
+import inspect
import os
import re
import sys
+from typing import Any, Dict, Iterable, List, Optional, Tuple
import unittest
from docutils import nodes
@@ -25,12 +29,20 @@ from six.moves import builtins, reduce
from sphinx.domains import std
from sphinx.util.nodes import nested_parse_with_titles
-__all__ = ('AutoprogramDirective',
- 'AutoprogramDirectiveTestCase', 'ScannerTestCase',
- 'import_object', 'scan_programs', 'setup', 'suite')
+__all__ = (
+ "AutoprogramDirective",
+ "AutoprogramDirectiveTestCase",
+ "ScannerTestCase",
+ "import_object",
+ "scan_programs",
+ "setup",
+ "suite",
+)
-def get_subparser_action(parser):
+def get_subparser_action(
+ parser: argparse.ArgumentParser,
+) -> Optional[argparse._SubParsersAction]:
neg1_action = parser._actions[-1]
if isinstance(neg1_action, argparse._SubParsersAction):
@@ -40,8 +52,16 @@ def get_subparser_action(parser):
if isinstance(a, argparse._SubParsersAction):
return a
+ return None
+
-def scan_programs(parser, command=[], maxdepth=0, depth=0, groups=False):
+def scan_programs(
+ parser: argparse.ArgumentParser,
+ command=[],
+ maxdepth: int = 0,
+ depth: int = 0,
+ groups: bool = False,
+):
if maxdepth and depth >= maxdepth:
return
@@ -56,29 +76,28 @@ def scan_programs(parser, command=[], maxdepth=0, depth=0, groups=False):
yield command, options, parser
if parser._subparsers:
- choices = ()
+ choices: Iterable[Tuple[Any, Any]] = ()
subp_action = get_subparser_action(parser)
if subp_action:
choices = subp_action.choices.items()
- if not (hasattr(collections, 'OrderedDict') and
- isinstance(choices, collections.OrderedDict)):
+ if not (
+ hasattr(collections, "OrderedDict")
+ and isinstance(choices, collections.OrderedDict)
+ ):
choices = sorted(choices, key=lambda pair: pair[0])
for cmd, sub in choices:
if isinstance(sub, argparse.ArgumentParser):
- for program in scan_programs(
- sub, command + [cmd], maxdepth, depth + 1
- ):
+ for program in scan_programs(sub, command + [cmd], maxdepth, depth + 1):
yield program
def scan_options(actions):
for arg in actions:
- if not (arg.option_strings or
- isinstance(arg, argparse._SubParsersAction)):
+ if not (arg.option_strings or isinstance(arg, argparse._SubParsersAction)):
yield format_positional_argument(arg)
for arg in actions:
@@ -86,36 +105,36 @@ def scan_options(actions):
yield format_option(arg)
-def format_positional_argument(arg):
- desc = (arg.help or '') % {'default': arg.default}
+def format_positional_argument(arg) -> Tuple[List[str], str]:
+ desc = (arg.help or "") % {"default": arg.default}
name = (arg.metavar or arg.dest).lower()
return [name], desc
-def format_option(arg):
- desc = (arg.help or '') % {'default': arg.default}
+def format_option(arg) -> Tuple[List[str], str]:
+ desc = (arg.help or "") % {"default": arg.default}
if not isinstance(arg, (argparse._StoreAction, argparse._AppendAction)):
names = list(arg.option_strings)
return names, desc
if arg.choices is not None:
- value = '{{{0}}}'.format(','.join(arg.choices))
+ value = "{{{0}}}".format(",".join(str(c) for c in arg.choices))
else:
metavar = arg.metavar or arg.dest
if not isinstance(metavar, tuple):
- metavar = metavar,
- value = '<{0}>'.format('> <'.join(metavar).lower())
+ metavar = (metavar,)
+ value = "<{0}>".format("> <".join(metavar).lower())
- names = ['{0} {1}'.format(option_string, value)
- for option_string in arg.option_strings]
+ names = [
+ "{0} {1}".format(option_string, value) for option_string in arg.option_strings
+ ]
return names, desc
-
-def import_object(import_name):
- module_name, expr = import_name.split(':', 1)
+def import_object(import_name: str):
+ module_name, expr = import_name.split(":", 1)
try:
mod = __import__(module_name)
except ImportError:
@@ -140,10 +159,10 @@ def import_object(import_name):
mod = __import__("foo")
break
else:
- raise ImportError("No module named {}".format(module_name))
+ raise
- mod = reduce(getattr, module_name.split('.')[1:], mod)
- globals_ = builtins
+ mod = reduce(getattr, module_name.split(".")[1:], mod)
+ globals_: Dict[str, Any] = builtins # type: ignore[assignment]
if not isinstance(globals_, dict):
globals_ = globals_.__dict__
return eval(expr, globals_, mod.__dict__)
@@ -154,37 +173,38 @@ class AutoprogramDirective(Directive):
has_content = False
required_arguments = 1
option_spec = {
- 'prog': unchanged,
- 'maxdepth': unchanged,
- 'start_command': unchanged,
- 'strip_usage': unchanged,
- 'no_usage_codeblock': unchanged,
- 'groups': unchanged,
+ "prog": unchanged,
+ "maxdepth": unchanged,
+ "start_command": unchanged,
+ "strip_usage": unchanged,
+ "no_usage_codeblock": unchanged,
+ "groups": unchanged,
}
def make_rst(self):
- import_name, = self.arguments
- parser = import_object(import_name or '__undefined__')
- prog = self.options.get('prog')
+ (import_name,) = self.arguments
+ parser = import_object(import_name or "__undefined__")
+ prog = self.options.get("prog")
if prog:
original_prog = parser.prog
parser.prog = prog
- start_command = self.options.get('start_command', '').split(' ')
- strip_usage = 'strip_usage' in self.options
- usage_codeblock = 'no_usage_codeblock' not in self.options
- maxdepth = int(self.options.get('maxdepth', 0))
- groups = 'groups' in self.options
+ start_command = self.options.get("start_command", "").split(" ")
+ strip_usage = "strip_usage" in self.options
+ usage_codeblock = "no_usage_codeblock" not in self.options
+ maxdepth = int(self.options.get("maxdepth", 0))
+ groups = "groups" in self.options
- if start_command[0] == '':
+ if start_command[0] == "":
start_command.pop(0)
if start_command:
+
def get_start_cmd_parser(p):
looking_for = start_command.pop(0)
action = get_subparser_action(p)
if not action:
- raise ValueError('No actions for command ' + looking_for)
+ raise ValueError("No actions for command " + looking_for)
subp = action.choices[looking_for]
@@ -210,8 +230,7 @@ class AutoprogramDirective(Directive):
else:
cmd_parser = group_or_parser
if prog and cmd_parser.prog.startswith(original_prog):
- cmd_parser.prog = cmd_parser.prog.replace(
- original_prog, prog, 1)
+ cmd_parser.prog = cmd_parser.prog.replace(original_prog, prog, 1)
title = cmd_parser.prog.rstrip()
description = cmd_parser.description
usage = cmd_parser.format_usage()
@@ -219,14 +238,17 @@ class AutoprogramDirective(Directive):
is_subgroup = bool(commands)
is_program = True
- for line in render_rst(title, options,
- is_program=is_program,
- is_subgroup=is_subgroup,
- description=description,
- usage=usage,
- usage_strip=strip_usage,
- usage_codeblock=usage_codeblock,
- epilog=epilog):
+ for line in render_rst(
+ title,
+ options,
+ is_program=is_program,
+ is_subgroup=is_subgroup,
+ description=description,
+ usage=usage,
+ usage_strip=strip_usage,
+ usage_codeblock=usage_codeblock,
+ epilog=epilog,
+ ):
yield line
def run(self):
@@ -234,61 +256,71 @@ class AutoprogramDirective(Directive):
node.document = self.state.document
result = ViewList()
for line in self.make_rst():
- result.append(line, '<autoprogram>')
+ result.append(line, "<autoprogram>")
nested_parse_with_titles(self.state, result, node)
return node.children
-def render_rst(title, options, is_program, is_subgroup, description,
- usage, usage_strip, usage_codeblock, epilog):
+def render_rst(
+ title: str,
+ options,
+ is_program: bool,
+ is_subgroup: bool,
+ description: Optional[str],
+ usage: str,
+ usage_strip: bool,
+ usage_codeblock: bool,
+ epilog: Optional[str],
+) -> Iterable[str]:
if usage_strip:
- to_strip = title.rsplit(' ', 1)[0]
+ to_strip = title.rsplit(" ", 1)[0]
len_to_strip = len(to_strip) - 4
usage_lines = usage.splitlines()
- usage = os.linesep.join([
- usage_lines[0].replace(to_strip, '...'),
- ] + [
- l[len_to_strip:] for l in usage_lines[1:]
- ])
+ usage = os.linesep.join(
+ [
+ usage_lines[0].replace(to_strip, "..."),
+ ]
+ + [line[len_to_strip:] for line in usage_lines[1:]]
+ )
- yield ''
+ yield ""
if is_program:
- yield '.. program:: ' + title
- yield ''
+ yield ".. program:: " + title
+ yield ""
yield title
- yield ('!' if is_subgroup else '?') * len(title)
- yield ''
+ yield ("!" if is_subgroup else "?") * len(title)
+ yield ""
- for line in (description or '').splitlines():
+ for line in inspect.cleandoc(description or "").splitlines():
yield line
- yield ''
+ yield ""
if usage is None:
pass
elif usage_codeblock:
- yield '.. code-block:: console'
- yield ''
+ yield ".. code-block:: console"
+ yield ""
for usage_line in usage.splitlines():
- yield ' ' + usage_line
+ yield " " + usage_line
else:
yield usage
- yield ''
+ yield ""
for option_strings, help_ in options:
- yield '.. option:: {0}'.format(', '.join(option_strings))
- yield ''
- yield ' ' + help_.replace('\n', ' \n')
- yield ''
+ yield ".. option:: {0}".format(", ".join(option_strings))
+ yield ""
+ yield " " + help_.replace("\n", " \n")
+ yield ""
- for line in (epilog or '').splitlines():
- yield line or ''
+ for line in (epilog or "").splitlines():
+ yield line or ""
-def patch_option_role_to_allow_argument_form():
+def patch_option_role_to_allow_argument_form() -> None:
"""Before Sphinx 1.2.2, :rst:dir:`.. option::` directive hadn't
allowed to not start with a dash or slash, so it hadn't been possible
to represent positional arguments (not options).
@@ -298,115 +330,139 @@ def patch_option_role_to_allow_argument_form():
It monkeypatches the :rst:dir:`.. option::` directive's behavior.
"""
- std.option_desc_re = re.compile(r'((?:/|-|--)?[-_a-zA-Z0-9]+)(\s*.*)')
+ std.option_desc_re = re.compile(r"((?:/|-|--)?[-_a-zA-Z0-9]+)(\s*.*)")
-def setup(app):
- app.add_directive('autoprogram', AutoprogramDirective)
+def setup(app) -> Dict[str, bool]:
+ app.add_directive("autoprogram", AutoprogramDirective)
patch_option_role_to_allow_argument_form()
+ return {
+ "parallel_read_safe": True,
+ "parallel_write_safe": True,
+ }
class ScannerTestCase(unittest.TestCase):
-
- def test_simple_parser(self):
- parser = argparse.ArgumentParser(description='Process some integers.')
- parser.add_argument('integers', metavar='N', type=int, nargs='*',
- help='an integer for the accumulator')
- parser.add_argument('-i', '--identity', type=int, default=0,
- help='the default result for no arguments '
- '(default: 0)')
- parser.add_argument('--sum', dest='accumulate', action='store_const',
- const=sum, default=max,
- help='sum the integers (default: find the max)')
- parser.add_argument('--key-value', metavar=('KEY', 'VALUE'), nargs=2)
- parser.add_argument('--max', help=argparse.SUPPRESS) # must be opt-out
+ def test_simple_parser(self) -> None:
+ parser = argparse.ArgumentParser(description="Process some integers.")
+ parser.add_argument(
+ "integers",
+ metavar="N",
+ type=int,
+ nargs="*",
+ help="an integer for the accumulator",
+ )
+ parser.add_argument(
+ "-i",
+ "--identity",
+ type=int,
+ default=0,
+ help="the default result for no arguments " "(default: 0)",
+ )
+ parser.add_argument(
+ "--sum",
+ dest="accumulate",
+ action="store_const",
+ const=sum,
+ default=max,
+ help="sum the integers (default: find the max)",
+ )
+ parser.add_argument("--key-value", metavar=("KEY", "VALUE"), nargs=2)
+ parser.add_argument("--max", help=argparse.SUPPRESS) # must be opt-out
programs = scan_programs(parser)
programs = list(programs)
self.assertEqual(1, len(programs))
- parser_info, = programs
+ (parser_info,) = programs
program, options, cmd_parser = parser_info
self.assertEqual([], program)
- self.assertEqual('Process some integers.', cmd_parser.description)
+ self.assertEqual("Process some integers.", cmd_parser.description)
self.assertEqual(5, len(options))
+ self.assertEqual((["n"], "an integer for the accumulator"), options[0])
self.assertEqual(
- (['n'], 'an integer for the accumulator'),
- options[0]
- )
- self.assertEqual(
- (['-h', '--help'], 'show this help message and exit'),
- options[1]
+ (["-h", "--help"], "show this help message and exit"), options[1]
)
self.assertEqual(
- (['-i <identity>', '--identity <identity>'],
- 'the default result for no arguments (default: 0)'),
- options[2]
+ (
+ ["-i <identity>", "--identity <identity>"],
+ "the default result for no arguments (default: 0)",
+ ),
+ options[2],
)
self.assertEqual(
- (['--sum'], 'sum the integers (default: find the max)'),
- options[3]
+ (["--sum"], "sum the integers (default: find the max)"), options[3]
)
self.assertEqual(
- (['--key-value <key> <value>', ], ''),
- options[4]
+ (
+ [
+ "--key-value <key> <value>",
+ ],
+ "",
+ ),
+ options[4],
)
- def test_subcommands(self):
- parser = argparse.ArgumentParser(description='Process some integers.')
+ def test_subcommands(self) -> None:
+ parser = argparse.ArgumentParser(description="Process some integers.")
subparsers = parser.add_subparsers()
- max_parser = subparsers.add_parser('max', description='Find the max.')
+ max_parser = subparsers.add_parser("max", description="Find the max.")
max_parser.set_defaults(accumulate=max)
- max_parser.add_argument('integers', metavar='N', type=int, nargs='+',
- help='An integer for the accumulator.')
- sum_parser = subparsers.add_parser('sum',
- description='Sum the integers.')
+ max_parser.add_argument(
+ "integers",
+ metavar="N",
+ type=int,
+ nargs="+",
+ help="An integer for the accumulator.",
+ )
+ sum_parser = subparsers.add_parser("sum", description="Sum the integers.")
sum_parser.set_defaults(accumulate=sum)
- sum_parser.add_argument('integers', metavar='N', type=int, nargs='+',
- help='An integer for the accumulator.')
+ sum_parser.add_argument(
+ "integers",
+ metavar="N",
+ type=int,
+ nargs="+",
+ help="An integer for the accumulator.",
+ )
programs = scan_programs(parser)
programs = list(programs)
self.assertEqual(3, len(programs))
# main
program, options, cmd_parser = programs[0]
self.assertEqual([], program)
- self.assertEqual('Process some integers.', cmd_parser.description)
+ self.assertEqual("Process some integers.", cmd_parser.description)
self.assertEqual(1, len(options))
self.assertEqual(
- (['-h', '--help'],
- 'show this help message and exit'),
- options[0]
+ (["-h", "--help"], "show this help message and exit"), options[0]
)
# max
program, options, cmd_parser = programs[1]
- self.assertEqual(['max'], program)
- self.assertEqual('Find the max.', cmd_parser.description)
+ self.assertEqual(["max"], program)
+ self.assertEqual("Find the max.", cmd_parser.description)
self.assertEqual(2, len(options))
- self.assertEqual((['n'], 'An integer for the accumulator.'),
- options[0])
+ self.assertEqual((["n"], "An integer for the accumulator."), options[0])
self.assertEqual(
- (['-h', '--help'],
- 'show this help message and exit'),
- options[1]
+ (["-h", "--help"], "show this help message and exit"), options[1]
)
# sum
program, options, cmd_parser = programs[2]
- self.assertEqual(['sum'], program)
- self.assertEqual('Sum the integers.', cmd_parser.description)
+ self.assertEqual(["sum"], program)
+ self.assertEqual("Sum the integers.", cmd_parser.description)
self.assertEqual(2, len(options))
- self.assertEqual((['n'], 'An integer for the accumulator.'),
- options[0])
-
- def test_argument_groups(self):
- parser = argparse.ArgumentParser(description='This is a program.')
- parser.add_argument('-v', action='store_true',
- help='A global argument')
- plain_group = parser.add_argument_group('Plain Options',
- description='This is a group.')
- plain_group.add_argument('--plain', action='store_true',
- help='A plain argument.')
- fancy_group = parser.add_argument_group('Fancy Options',
- description='Another group.')
- fancy_group.add_argument('fancy', type=int, help='Set the fancyness')
+ self.assertEqual((["n"], "An integer for the accumulator."), options[0])
+
+ def test_argument_groups(self) -> None:
+ parser = argparse.ArgumentParser(description="This is a program.")
+ parser.add_argument("-v", action="store_true", help="A global argument")
+ plain_group = parser.add_argument_group(
+ "Plain Options", description="This is a group."
+ )
+ plain_group.add_argument(
+ "--plain", action="store_true", help="A plain argument."
+ )
+ fancy_group = parser.add_argument_group(
+ "Fancy Options", description="Another group."
+ )
+ fancy_group.add_argument("fancy", type=int, help="Set the fancyness")
sections = list(scan_programs(parser, groups=True))
self.assertEqual(4, len(sections))
@@ -414,91 +470,129 @@ class ScannerTestCase(unittest.TestCase):
# section: unnamed
program, options, cmd_parser = sections[0]
self.assertEqual([], program)
- self.assertEqual('This is a program.', cmd_parser.description)
+ self.assertEqual("This is a program.", cmd_parser.description)
self.assertEqual(0, len(options))
# section: default optionals
program, options, group = sections[1]
self.assertEqual([], program)
- self.assertEqual('optional arguments', group.title)
+ self.assertEqual("optional arguments", group.title)
self.assertEqual(None, group.description)
self.assertEqual(2, len(options))
- self.assertEqual((['-h', '--help'], 'show this help message and exit'),
- options[0])
- self.assertEqual((['-v'], 'A global argument'), options[1])
+ self.assertEqual(
+ (["-h", "--help"], "show this help message and exit"), options[0]
+ )
+ self.assertEqual((["-v"], "A global argument"), options[1])
# section: Plain Options
program, options, group = sections[2]
self.assertEqual([], program)
- self.assertEqual('Plain Options', group.title)
- self.assertEqual('This is a group.', group.description)
+ self.assertEqual("Plain Options", group.title)
+ self.assertEqual("This is a group.", group.description)
self.assertEqual(1, len(options))
- self.assertEqual((['--plain'], 'A plain argument.'), options[0])
+ self.assertEqual((["--plain"], "A plain argument."), options[0])
# section: Fancy Options
program, options, group = sections[3]
self.assertEqual([], program)
- self.assertEqual('Fancy Options', group.title)
- self.assertEqual('Another group.', group.description)
+ self.assertEqual("Fancy Options", group.title)
+ self.assertEqual("Another group.", group.description)
self.assertEqual(1, len(options))
- self.assertEqual((['fancy'], 'Set the fancyness'), options[0])
+ self.assertEqual((["fancy"], "Set the fancyness"), options[0])
- def test_choices(self):
+ def test_choices(self) -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--awesomeness", choices=["meh", "awesome"])
- program, options, cmd_parser = list(scan_programs(parser))[0]
+ _program, options, _cmd_parser = list(scan_programs(parser))[0]
log_option = options[1]
- self.assertEqual((["--awesomeness {meh,awesome}"], ''), log_option)
+ self.assertEqual((["--awesomeness {meh,awesome}"], ""), log_option)
- def test_parse_epilog(self):
+ def test_parse_epilog(self) -> None:
parser = argparse.ArgumentParser(
- description='Process some integers.',
- epilog='The integers will be processed.'
+ description="Process some integers.",
+ epilog="The integers will be processed.",
)
programs = scan_programs(parser)
programs = list(programs)
self.assertEqual(1, len(programs))
- parser_data, = programs
- program, options, cmd_parser = parser_data
- self.assertEqual('The integers will be processed.', cmd_parser.epilog)
+ (parser_data,) = programs
+ _program, _options, cmd_parser = parser_data
+ self.assertEqual("The integers will be processed.", cmd_parser.epilog)
class AutoprogramDirectiveTestCase(unittest.TestCase):
-
- def setUp(self):
+ def setUp(self) -> None:
self.untouched_sys_path = sys.path[:]
- sample_prog_path = os.path.join(os.path.dirname(__file__), '..', 'doc')
+ sample_prog_path = os.path.join(os.path.dirname(__file__), "..", "doc")
sys.path.insert(0, sample_prog_path)
self.directive = AutoprogramDirective(
- 'autoprogram', ['cli:parser'], {'prog': 'cli.py'},
- StringList([], items=[]), 1, 0,
- '.. autoprogram:: cli:parser\n :prog: cli.py\n',
- None, None
+ "autoprogram",
+ ["cli:parser"],
+ {"prog": "cli.py"},
+ StringList([], items=[]),
+ 1,
+ 0,
+ ".. autoprogram:: cli:parser\n :prog: cli.py\n",
+ None,
+ None,
)
- def tearDown(self):
+ def tearDown(self) -> None:
sys.path[:] = self.untouched_sys_path
- def test_make_rst(self):
- """Alt least it shouldn't raise errors during making RST string."""
- list(self.directive.make_rst())
+ def test_make_rst(self) -> None:
+ self.assertEqual(
+ "\n".join(self.directive.make_rst()).strip(),
+ inspect.cleandoc(
+ """
+ .. program:: cli.py
+ cli.py
+ ??????
-class UtilTestCase(unittest.TestCase):
+ Process some integers.
+
+ .. code-block:: console
+
+ usage: cli.py [-h] [-i IDENTITY] [--sum] N [N ...]
+
+ .. option:: n
+
+ An integer for the accumulator.
+
+ .. option:: -h, --help
+
+ show this help message and exit
- def test_import_object(self):
- cls = import_object('sphinxcontrib.autoprogram:UtilTestCase')
+ .. option:: -i <identity>, --identity <identity>
+
+ the default result for no arguments (default: 0)
+
+ .. option:: --sum
+
+ Sum the integers (default: find the max).
+ """).strip()
+ )
+
+
+
+class UtilTestCase(unittest.TestCase):
+ def test_import_object(self) -> None:
+ cls = import_object("sphinxcontrib.autoprogram:UtilTestCase")
self.assertTrue(cls is UtilTestCase)
instance = import_object(
'sphinxcontrib.autoprogram:UtilTestCase("test_import_object")'
)
self.assertIsInstance(instance, UtilTestCase)
- if not hasattr(unittest.TestCase, 'assertIsInstance'):
- def assertIsInstance(self, instance, cls):
- self.assertTrue(isinstance(instance, cls),
- '{0!r} is not an instance of {1.__module__}.'
- '{1.__name__}'.format(instance, cls))
+ if not hasattr(unittest.TestCase, "assertIsInstance"):
+
+ def assertIsInstance(self, instance, cls) -> None: # type: ignore[override]
+ self.assertTrue(
+ isinstance(instance, cls),
+ "{0!r} is not an instance of {1.__module__}."
+ "{1.__name__}".format(instance, cls),
+ )
suite = unittest.TestSuite()
=====================================
tox.ini
=====================================
@@ -1,22 +1,28 @@
+# Policy:
+#
+# Always support at least two major versions of Python and Sphinx: The latest,
+# and the one just before that. For the latest major version of Sphinx, test
+# against the latest three minor versions. For older major versions, test
+# against the latest minor version until maintenance becomes a burden, then drop
+# them.
+
[tox]
envlist =
- {py27,py33,py34,py35,py36,pypy}-{sphinx17,sphinx16,sphinx15}
- py26-{sphinx16,sphinx15,sphinx14,sphinx13,sphinx12}
- lint
+ {py37,py38,py39}-{sphinx34,sphinx33,sphinx32,sphinx24,sphinx18}
+ black flake8 mypy pylint
minversion = 2.7.0
[testenv]
deps =
- sphinx17: Sphinx >= 1.7.0, < 1.8.0
- sphinx16: Sphinx >= 1.6.0, < 1.7.0
- sphinx15: Sphinx >= 1.5.0, < 1.6.0
- sphinx14: Sphinx >= 1.4.0, < 1.5.0
- sphinx13: Sphinx >= 1.3.0, < 1.4.0
- sphinx12: Sphinx >= 1.2.0, < 1.3.0
+ sphinx34: Sphinx >= 3.4.0, < 3.5.0
+ sphinx33: Sphinx >= 3.3.0, < 3.4.0
+ sphinx32: Sphinx >= 3.2.0, < 3.3.0
+ sphinx24: Sphinx >= 2.4.0, < 3.0.0
+ sphinx18: Sphinx >= 1.8.0, < 1.9.0
commands =
python setup.py test
-[testenv:lint]
+[testenv:flake8]
deps =
flake8 >= 3.5.0, < 4.0.0
flake8-import-order-spoqa >= 1.3.0, < 2.0.0
@@ -24,12 +30,31 @@ commands =
flake8 sphinxcontrib/
[flake8]
-exclude = .tox, doc
+exclude = build, dist, doc, .tox
import-order-style = spoqa
application-import-names = sphinxcontrib.autoprogram
+max-line-length = 88
[testenv:doc]
basepython = python3
deps = -rdoc/rtd-requires.txt
commands =
python3 setup.py build_sphinx --build-dir=doc/_build
+
+[testenv:black]
+deps =
+ black
+commands =
+ black --check --diff sphinxcontrib
+
+[testenv:mypy]
+deps =
+ mypy
+commands =
+ mypy sphinxcontrib
+
+[testenv:pylint]
+deps =
+ pylint
+commands =
+ pylint sphinxcontrib
\ No newline at end of file
View it on GitLab: https://salsa.debian.org/med-team/sphinxcontrib-autoprogram/-/compare/096c86ae9ff65efcaad20036ccdd5c658d6ad050...46e643de491b1d259d4acec66d7ab29229c57a6f
--
View it on GitLab: https://salsa.debian.org/med-team/sphinxcontrib-autoprogram/-/compare/096c86ae9ff65efcaad20036ccdd5c658d6ad050...46e643de491b1d259d4acec66d7ab29229c57a6f
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20211007/a6329459/attachment-0001.htm>
More information about the debian-med-commit
mailing list