diff --git a/.gitignore b/.gitignore index af2535b..a31cdac 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ tests/test_docs/_build/ .pytest_cache/ .vscode/ _build +htmlcov +.coverage diff --git a/.travis.yml b/.travis.yml index 429bcb2..bbb6a28 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,11 @@ sudo: false dist: xenial language: python -python: - - "2.7" +python: - "3.6" - "3.7" - "3.8" + - "3.9" install: - pip install tox-travis script: diff --git a/CHANGES.rst b/CHANGES.rst index 3d02ac7..79080cc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,20 @@ +sphinxcontrib-matlabdomain-0.12.0 (2021-06-12) +============================================== + +* Only Sphinx >= 4.0.0 is now supported. +* Only Python >= 3.6 is supported. +* Fixed numerous warnings due to `deprecated Sphinx API`_. + * Use ``sphinx.ext.autodoc.directive.DocumenterBridge.record_dependencies`` + insted of ``sphinx.ext.autodoc.directive.DocumenterBridge.filename_set``. + * Use ``str.rpartition()`` insted of ``sphinx.util.rpartition()`` + * Remove use of ``sphinx.util.force_decode()``. + * Use ``inspect.getmembers()`` insted of + ``sphinx.util.inspect.safe_getmembers()``. + * Remove use of encoding argument in ``autodoc.Documenter.get_doc()``. +* Fixed `Issue 101 `_. +* CI now tests on Python 3.6, 3.7, 3.8 and 3.9. + + sphinxcontrib-matlabdomain-0.11.8 (2021-05-12) ============================================== @@ -356,3 +373,5 @@ sphinxcontrib-matlabdomain-0.1 (2013-04-25) * create a Sphinx domain for MATLAB * override standard domain to remove py modules index + +.. _`deprecated Sphinx API`: https://www.sphinx-doc.org/en/master/extdev/deprecated.html \ No newline at end of file diff --git a/README.rst b/README.rst index 46907a3..4fef135 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,8 @@ The Python package must be installed with:: pip install -U sphinxcontrib-matlabdomain -In general, the usage is the same as for documenting Python code. +In general, the usage is the same as for documenting Python code. The package +requires Python >= 3.6 and Sphinx >= 4.0.0. For a Python 2 compatible version the package must be installed with:: diff --git a/pytest.ini b/pytest.ini index 07802a8..cabfb2b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,2 +1,2 @@ [pytest] -log_print = False +addopts = --show-capture=all diff --git a/setup.py b/setup.py index bd58417..01032fa 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open('README.rst', 'r') as f_readme: long_desc = f_readme.read() -requires = ['Sphinx<4.0.0>=1.72', 'Pygments>=2.0.1', 'future>=0.16.0'] +requires = ['Sphinx>=4.0.0', 'Pygments>=2.0.1', 'future>=0.16.0'] setup( name='sphinxcontrib-matlabdomain', diff --git a/sphinxcontrib/mat_directives.py b/sphinxcontrib/mat_directives.py index 70c55ab..f0271bc 100644 --- a/sphinxcontrib/mat_directives.py +++ b/sphinxcontrib/mat_directives.py @@ -1,11 +1,9 @@ from __future__ import unicode_literals from sphinx.ext.autodoc.directive import AutodocDirective, DummyOptionSpec, DocumenterBridge from sphinx.ext.autodoc.directive import process_documenter_options, parse_generated_content -from sphinx.util import logging -from sphinx import version_info - -logger = logging.getLogger('matlab-domain') +import sphinx.util +logger = sphinx.util.logging.getLogger('matlab-domain') class MatlabAutodocDirective(AutodocDirective): """A directive class for all MATLAB autodoc directives. @@ -44,11 +42,8 @@ def run(self): (self.name, exc), location=(source, lineno)) return [] - # generate the output - if version_info[0] >= 2: - params = DocumenterBridge(self.env, reporter, documenter_options, lineno, self.state) - else: - params = DocumenterBridge(self.env, reporter, documenter_options, lineno) + # generate the output + params = DocumenterBridge(self.env, reporter, documenter_options, lineno, self.state) documenter = doccls(params, self.arguments[0]) documenter.generate(more_content=self.content) if not params.result: @@ -58,7 +53,7 @@ def run(self): # record all filenames as dependencies -- this will at least # partially make automatic invalidation possible - for fn in params.filename_set: + for fn in params.record_dependencies: self.state.document.settings.record_dependencies.add(fn) result = parse_generated_content(self.state, params.result, documenter) diff --git a/sphinxcontrib/mat_documenters.py b/sphinxcontrib/mat_documenters.py index b72c6ed..aa022a4 100644 --- a/sphinxcontrib/mat_documenters.py +++ b/sphinxcontrib/mat_documenters.py @@ -14,29 +14,16 @@ MatMethod, MatScript, MatException, MatModuleAnalyzer, MatApplication, modules) - import re import sys import traceback +import inspect from docutils.statemachine import ViewList -from sphinx.util import rpartition, force_decode +import sphinx.util from sphinx.locale import _ from sphinx.pycode import PycodeError -# from sphinx.util.nodes import nested_parse_with_titles -# from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \ -# safe_getattr, is_builtin_class_method -from sphinx.util.inspect import safe_getmembers, safe_getattr -from sphinx.util import logging -try: - # Sphinx >= 1.3.0 - from sphinx.util.inspect import object_description -except ImportError: - # Sphinx < 1.3.0 - from sphinx.util.inspect import safe_repr as object_description -from sphinx.util.docstrings import prepare_docstring - from sphinx.ext.autodoc import py_ext_sig_re as mat_ext_sig_re, \ identity, Options, ALL, INSTANCEATTR, members_option, \ members_set_option, SUPPRESS, annotation_option, bool_option, \ @@ -61,7 +48,7 @@ # TODO: check MRO's for all classes, attributes and methods!!! -logger = logging.getLogger('matlab-domain') +logger = sphinx.util.logging.getLogger('matlab-domain') class MatlabDocumenter(PyDocumenter): @@ -82,7 +69,7 @@ def parse_name(self): try: explicit_modname, path, base, args, retann = \ mat_ext_sig_re.match(self.name).groups() - except AttributeError: + except AttributeError: self.directive.warn('invalid signature for auto%s (%r)' % (self.objtype, self.name)) return False @@ -148,9 +135,8 @@ def import_object(self): errmsg = '[sphinxcontrib-matlabdomain]: failed to import %s %r' % \ (self.objtype, self.fullname) errmsg += '; the following exception was raised:\n%s' % \ - traceback.format_exc() - logger.debug(errmsg) - self.directive.warn(errmsg) + traceback.format_exc() + logger.warning(errmsg) self.env.note_reread() return False @@ -222,7 +208,7 @@ def get_object_members(self, want_all): elif self.options.inherited_members: # safe_getmembers() uses dir() which pulls in members from all # base classes - members = safe_getmembers(self.object, attr_getter=self.get_attr) + members = inspect.get_members(self.object, attr_getter=self.get_attr) else: # __dict__ contains only the members directly defined in # the class (but get them via getattr anyway, to e.g. get @@ -533,9 +519,9 @@ def generate(self, more_content=None, real_modname=None, self.analyzer = None # at least add the module.__file__ as a dependency if hasattr(self.module, '__file__') and self.module.__file__: - self.directive.filename_set.add(self.module.__file__) + self.directive.record_dependencies.add(self.module.__file__) else: - self.directive.filename_set.add(self.analyzer.srcname) + self.directive.record_dependencies.add(self.analyzer.srcname) # check __module__ of object (for members not given explicitly) if check_module: @@ -608,7 +594,7 @@ def get_object_members(self, want_all): self.directive.warn( 'missing attribute mentioned in :members: or __all__: ' 'module %s, attribute %s' % ( - safe_getattr(self.object, '__name__', '???'), mname)) + sphinx.util.inspect.safe_getattr(self.object, '__name__', '???'), mname)) return False, ret @@ -653,7 +639,7 @@ def resolve_name(self, modname, parents, path, base): # ... if still None, there's no way to know if mod_cls is None: return None, [] - modname, cls = rpartition(mod_cls, '.') + modname, _, cls = mod_cls.rpartition('.') parents = [cls] # if the module name is still missing, get it like above if not modname: @@ -687,7 +673,7 @@ def _find_signature(self, encoding=None): if not self.objpath or base != self.objpath[-1]: return # re-prepare docstring to ignore indentation after signature - docstrings = MatlabDocumenter.get_doc(self, encoding, 2) + docstrings = MatlabDocumenter.get_doc(self, encoding) doclines = docstrings[0] # ok, now jump over remaining empty lines and set the remaining # lines as the new doclines @@ -697,11 +683,11 @@ def _find_signature(self, encoding=None): setattr(self, '__new_doclines', doclines[i:]) return args, retann - def get_doc(self, encoding=None, ignore=1): + def get_doc(self, encoding=None): lines = getattr(self, '__new_doclines', None) if lines is not None: return [lines] - return MatlabDocumenter.get_doc(self, encoding, ignore) + return MatlabDocumenter.get_doc(self, encoding) def format_signature(self): if self.args is None and self.env.config.autodoc_docstring_signature: @@ -829,7 +815,7 @@ def add_directive_header(self, sig): if base_class_links: self.add_line(_(' Bases: %s') % ', '.join(base_class_links), '') - def get_doc(self, encoding=None, ignore=1): + def get_doc(self, encoding=None): content = self.env.config.autoclass_content docstrings = [] @@ -864,14 +850,12 @@ def get_doc(self, encoding=None, ignore=1): docstrings.append(initdocstring) doc = [] for docstring in docstrings: - if not isinstance(docstring, str): - docstring = force_decode(docstring, encoding) - doc.append(prepare_docstring(docstring)) + doc.append(sphinx.util.docstrings.prepare_docstring(docstring)) return doc def add_content(self, more_content, no_docstring=False): if self.doc_as_attr: - classname = safe_getattr(self.object, '__name__', None) + classname = sphinx.util.inspect.safe_getattr(self.object, '__name__', None) if classname: content = ViewList( [_('alias of :class:`%s`') % classname], source='') @@ -975,7 +959,7 @@ def add_directive_header(self, sig): if not self.options.annotation: if not self._datadescriptor: try: - objrepr = object_description(self.object.default) # display default + objrepr = sphinx.util.inspect.object_description(self.object.default) # display default except ValueError: pass else: diff --git a/sphinxcontrib/mat_types.py b/sphinxcontrib/mat_types.py index faa7f9c..0eb4911 100644 --- a/sphinxcontrib/mat_types.py +++ b/sphinxcontrib/mat_types.py @@ -14,13 +14,13 @@ import re import sys from copy import copy -from sphinx.util import logging +import sphinx.util from sphinxcontrib.mat_lexer import MatlabLexer from pygments.token import Token from zipfile import ZipFile import xml.etree.ElementTree as ET -logger = logging.getLogger('matlab-domain') +logger = sphinx.util.logging.getLogger('matlab-domain') modules = {} diff --git a/sphinxcontrib/matlab.py b/sphinxcontrib/matlab.py index 8593c54..be781d0 100644 --- a/sphinxcontrib/matlab.py +++ b/sphinxcontrib/matlab.py @@ -24,6 +24,9 @@ from sphinx.directives import ObjectDescription from sphinx.util.nodes import make_refnode from sphinx.util.docfields import Field, GroupedField, TypedField +import sphinx.util + +logger = sphinx.util.logging.getLogger('matlab-domain') # REs for MATLAB signatures @@ -732,10 +735,9 @@ def resolve_xref(self, env, fromdocname, builder, if not matches: return None elif len(matches) > 1: - env.warn_node( - 'more than one target found for cross-reference ' - '%r: %s' % (target, ', '.join(match[0] for match in matches)), - node) + logger.warning('[sphinxcontrib-matlabdomain] more than one target found for cross-reference %r: %s', + target, ', '.join(match[0] for match in matches), type='ref', subtype='python', location=node) + name, obj = matches[0] if obj[1] == 'module': diff --git a/tests/roots/test_duplicate_link/.gitignore b/tests/roots/test_duplicate_link/.gitignore new file mode 100644 index 0000000..69fa449 --- /dev/null +++ b/tests/roots/test_duplicate_link/.gitignore @@ -0,0 +1 @@ +_build/ diff --git a/tests/roots/test_duplicate_link/Makefile b/tests/roots/test_duplicate_link/Makefile new file mode 100644 index 0000000..b657d66 --- /dev/null +++ b/tests/roots/test_duplicate_link/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = sphinxcontrib-matlabdomain +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/tests/roots/test_duplicate_link/_src/+replab/Domain.m b/tests/roots/test_duplicate_link/_src/+replab/Domain.m new file mode 100644 index 0000000..9bdfa85 --- /dev/null +++ b/tests/roots/test_duplicate_link/_src/+replab/Domain.m @@ -0,0 +1,22 @@ +classdef Domain +% Describes a set of elements with a common structure +% +% Those elements can be compared (`.eqv`), and random elements can be produced (`.sample`). + + methods % ABSTRACT + + function b = eqv(self, t, u) + % Tests domain elements for equality/equivalence + % + % Args: + % t (domain element): First element to test + % u (domain element): Second element to test + % + % Returns: + % logical: True when ``t`` and ``u`` are equivalent, and false otherwise + error('Abstract'); + end + + end + +end diff --git a/tests/roots/test_duplicate_link/_src/+replab/FiniteGroup.m b/tests/roots/test_duplicate_link/_src/+replab/FiniteGroup.m new file mode 100644 index 0000000..7cff630 --- /dev/null +++ b/tests/roots/test_duplicate_link/_src/+replab/FiniteGroup.m @@ -0,0 +1,11 @@ +classdef FiniteGroup < replab.CompactGroup +% Describes a group with a finite number of elements +% +% .. admonition:: Inherited elements +% :class: collapsed +% +% .. method:: eqv +% +% Documentation in :meth:`+replab.Domain.eqv` + +end diff --git a/tests/roots/test_duplicate_link/_src/+replab/Group.m b/tests/roots/test_duplicate_link/_src/+replab/Group.m new file mode 100644 index 0000000..0751dfc --- /dev/null +++ b/tests/roots/test_duplicate_link/_src/+replab/Group.m @@ -0,0 +1,10 @@ +classdef Group < replab.Monoid +% Describes a group +% +% .. admonition:: Inherited elements +% :class: collapsed +% +% .. method:: eqv +% +% Documentation in :meth:`+replab.Domain.eqv` +end diff --git a/tests/roots/test_duplicate_link/_src/+replab/Monoid.m b/tests/roots/test_duplicate_link/_src/+replab/Monoid.m new file mode 100644 index 0000000..8a5c552 --- /dev/null +++ b/tests/roots/test_duplicate_link/_src/+replab/Monoid.m @@ -0,0 +1,12 @@ +classdef Monoid < replab.Domain +% Describes a monoid +% +% See https://en.wikipedia.org/wiki/Monoid +% +% .. admonition:: Inherited elements +% +% .. method:: eqv +% +% Documentation in :meth:`+replab.Domain.eqv` + +end diff --git a/tests/roots/test_duplicate_link/_src/+replab/NiceFiniteGroup.m b/tests/roots/test_duplicate_link/_src/+replab/NiceFiniteGroup.m new file mode 100644 index 0000000..da95b72 --- /dev/null +++ b/tests/roots/test_duplicate_link/_src/+replab/NiceFiniteGroup.m @@ -0,0 +1,14 @@ +classdef NiceFiniteGroup < replab.FiniteGroup +% A nice finite group is a finite group equipped with an injective homomorphism into a permutation group +% +% Reference that triggers the error: `.eqv` + + methods + + function b = eqv(self, x, y) + b = self.parent.eqv(x, y); + end + + end + +end diff --git a/tests/roots/test_duplicate_link/base.rst b/tests/roots/test_duplicate_link/base.rst new file mode 100644 index 0000000..5928262 --- /dev/null +++ b/tests/roots/test_duplicate_link/base.rst @@ -0,0 +1,18 @@ +Base object types in RepLAB +=========================== + +.. module:: +replab + +.. _Str: + +Str ++++ + +.. autoclass:: Str + +.. _Domain: + +Domain +++++++ + +.. autoclass:: Domain diff --git a/tests/roots/test_duplicate_link/conf.py b/tests/roots/test_duplicate_link/conf.py new file mode 100644 index 0000000..226461c --- /dev/null +++ b/tests/roots/test_duplicate_link/conf.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/stable/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = 'RepLAB' +copyright = '2018-2019, Denis Rosset, Jean-Daniel Bancal and collaborators' +author = 'Denis Rosset, Jean-Daniel Bancal and collaborators' + +# The short X.Y version +version = '0.6' +# The full version, including alpha/beta/rc tags +release = '0.6.0-SNAPSHOT' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['sphinx.ext.autodoc', # for enumeration of objects stuff + 'sphinx.ext.autosummary', + 'sphinxcontrib.matlab', # support for Matlab +] + +autodoc_default_options = {'members': True, 'show-inheritance': True} +autosummary_generate = True + +matlab_keep_package_prefix = True + +matlab_src_dir = os.path.dirname(os.path.abspath(__file__ + "/"))+"/_src" +primary_domain = 'mat' +default_role = 'obj' + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path . +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' \ No newline at end of file diff --git a/tests/roots/test_duplicate_link/groups.rst b/tests/roots/test_duplicate_link/groups.rst new file mode 100644 index 0000000..f71aee0 --- /dev/null +++ b/tests/roots/test_duplicate_link/groups.rst @@ -0,0 +1,39 @@ +Abstract group structures in RepLAB +=================================== + +.. module:: +replab + +.. _Monoid: + +Monoid +++++++ + +.. autoclass:: Monoid + +.. _Group: + +Group ++++++ + +.. autoclass:: Group + +.. _CompactGroup: + +CompactGroup +++++++++++++ + +.. autoclass:: CompactGroup + +.. _FiniteGroup: + +FiniteGroup ++++++++++++ + +.. autoclass:: FiniteGroup + +.. _NiceFiniteGroup: + +NiceFiniteGroup ++++++++++++++++ + +.. autoclass:: NiceFiniteGroup diff --git a/tests/roots/test_duplicate_link/index.rst b/tests/roots/test_duplicate_link/index.rst new file mode 100644 index 0000000..f922780 --- /dev/null +++ b/tests/roots/test_duplicate_link/index.rst @@ -0,0 +1,15 @@ +RepLAB documentation +==================== + + +.. toctree:: + :maxdepth: 4 + + base + groups + +Indices and tables +================== +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/tests/roots/test_duplicate_link/make.bat b/tests/roots/test_duplicate_link/make.bat new file mode 100644 index 0000000..eebfde4 --- /dev/null +++ b/tests/roots/test_duplicate_link/make.bat @@ -0,0 +1,36 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build +set SPHINXPROJ=sphinxcontrib-matlabdomain + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/tests/test_duplicated_link.py b/tests/test_duplicated_link.py new file mode 100644 index 0000000..8db59fc --- /dev/null +++ b/tests/test_duplicated_link.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +""" + test_package_links.py + ~~~~~~~~~~~~ + + Test the autodoc extension. + + :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from __future__ import unicode_literals +import pickle +import os +import sys +import docutils + +import pytest + +from sphinx import addnodes +from sphinx import version_info +from sphinx.testing.fixtures import test_params, make_app +from sphinx.testing.path import path + + +@pytest.fixture(scope='module') +def rootdir(): + return path(os.path.dirname(__file__)).abspath() + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_with_prefix(make_app, rootdir): + srcdir = rootdir / 'roots' / 'test_duplicate_link' + app = make_app(srcdir=srcdir) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / 'groups.doctree').read_bytes()) + + assert isinstance(content[0], docutils.nodes.section) + section = content[0][7] + assert section.astext() == 'NiceFiniteGroup\n\n\n\nclass +replab.NiceFiniteGroup\n\nBases: +replab.FiniteGroup\n\nA nice finite group is a finite group equipped with an injective homomorphism into a permutation group\n\nReference that triggers the error: eqv' + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") +def test_without_prefix(make_app, rootdir): + srcdir = rootdir / 'roots' / 'test_duplicate_link' + confdict = { 'matlab_keep_package_prefix' : False } + app = make_app(srcdir=srcdir, confoverrides=confdict) + app.builder.build_all() + + content = pickle.loads((app.doctreedir / 'groups.doctree').read_bytes()) + + assert isinstance(content[0], docutils.nodes.section) + section = content[0][7] + assert section.astext() == 'NiceFiniteGroup\n\n\n\nclass replab.NiceFiniteGroup\n\nBases: replab.FiniteGroup\n\nA nice finite group is a finite group equipped with an injective homomorphism into a permutation group\n\nReference that triggers the error: eqv' + + +if __name__ == '__main__': + pytest.main([__file__]) \ No newline at end of file diff --git a/tox.ini b/tox.ini index d97dab2..910ca5e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py36,py37,p38 +envlist = py36,py37,p38,py39 [testenv] deps =