diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 4020d8d..823105d 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -7,12 +7,16 @@ jobs: build-n-publish: name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI runs-on: ubuntu-latest + environment: release + permissions: + # IMPORTANT: this permission is mandatory for trusted publishing + id-token: write steps: - uses: actions/checkout@v3 with: ref: main - - name: Set up Python 3.8 + - name: Set up Python 3.10 uses: actions/setup-python@v4 with: python-version: '3.10' @@ -27,7 +31,4 @@ jobs: python setup.py sdist bdist_wheel - name: Publish distribution to PyPI - uses: pypa/gh-action-pypi-publish@master - with: - user: __token__ - password: ${{ secrets.pypi_api_key }} + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/run_tox.yml b/.github/workflows/run_tox.yml index 7136e72..57edeb5 100644 --- a/.github/workflows/run_tox.yml +++ b/.github/workflows/run_tox.yml @@ -3,12 +3,22 @@ name: Tests on: [push, pull_request] jobs: + pre-commit: + name: pre-commit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + - uses: pre-commit/action@v3.0.0 + test: runs-on: ubuntu-latest strategy: max-parallel: 4 matrix: - python-version: ['3.8', '3.9', '3.10', '3.11'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1cb5edf..9a40a02 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,35 +1,27 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: + - id: check-ast + - id: check-builtin-literals + - id: check-docstring-first + - id: check-merge-conflict + - id: check-toml - id: check-yaml + - id: debug-statements - id: end-of-file-fixer - id: trailing-whitespace - - repo: https://github.com/pycqa/isort - rev: 5.12.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.11 # check that this version matches the requirements.txt hooks: - - id: isort - name: isort (python) - - - - repo: https://github.com/PyCQA/flake8 - rev: '6.0.0' - hooks: - - id: flake8 - additional_dependencies: - - flake8-bugbear==23.1.20 - - flake8-comprehensions==3.10.1 - - flake8-pytest-style==1.6 - - flake8-noqa==1.3 - - pep8-naming==0.13.3 + - id: ruff - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: - id: rst-backticks - - repo: meta hooks: - id: check-hooks-apply diff --git a/.readthedocs.yml b/.readthedocs.yml index 409b84d..c55a20b 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -18,9 +18,9 @@ formats: all build: - os: ubuntu-20.04 + os: ubuntu-22.04 tools: - python: "3.9" + python: "3.10" python: install: diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..d9d9603 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,85 @@ + +line-length = 120 +indent-width = 4 + +target-version = "py38" +src = ["src", "test"] + +# https://docs.astral.sh/ruff/settings/#ignore-init-module-imports +ignore-init-module-imports = true + +extend-exclude = ["__init__.py"] + +select = [ + "E", "W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w + "I", # https://docs.astral.sh/ruff/rules/#isort-i + "UP", # https://docs.astral.sh/ruff/rules/#pyupgrade-up + + "A", # https://docs.astral.sh/ruff/rules/#flake8-builtins-a + "ASYNC", # https://docs.astral.sh/ruff/rules/#flake8-async-async + "C4", # https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4 + "EM", # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em + "FIX", # https://docs.astral.sh/ruff/rules/#flake8-fixme-fix + "INP", # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp + "ISC", # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc + "PIE", # https://docs.astral.sh/ruff/rules/#flake8-pie-pie + "PT", # https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt + "PTH", # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth + "RET", # https://docs.astral.sh/ruff/rules/#flake8-return-ret + "SIM", # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim + "SLOT", # https://docs.astral.sh/ruff/rules/#flake8-slots-slot + "T10", # https://docs.astral.sh/ruff/rules/#flake8-debugger-t10 + "TCH", # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tch + "TD", # https://docs.astral.sh/ruff/rules/#flake8-todos-td + + "TRY", # https://docs.astral.sh/ruff/rules/#tryceratops-try + "FLY", # https://docs.astral.sh/ruff/rules/#flynt-fly + "PERF", # https://docs.astral.sh/ruff/rules/#perflint-perf + "RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf + + "PL", # https://docs.astral.sh/ruff/rules/#pylint-pl +] + +ignore = [ + "RET501", # https://docs.astral.sh/ruff/rules/unnecessary-return-none/#unnecessary-return-none-ret501 + "TRY400", # https://docs.astral.sh/ruff/rules/error-instead-of-exception/ + + "A003", # https://docs.astral.sh/ruff/rules/builtin-attribute-shadowing/ +] + + +[format] +# Use single quotes for non-triple-quoted strings. +quote-style = "single" + + + +[lint.per-file-ignores] +"doc/*" = [ + "A001", # A001 Variable `copyright` is shadowing a Python builtin + "E402", # E402 Module level import not at top of file + "INP001", # INP001 File `FILE_NAME` is part of an implicit namespace package. Add an `__init__.py`. + "PTH100", # PTH100 `os.path.abspath()` should be replaced by `Path.resolve()` + "PTH118", # PTH118 `os.path.join()` should be replaced by `Path` with `/` operator +] + +"tests/*" = [ + "INP001", # INP001 File `FILE_NAME` is part of an implicit namespace package. Add an `__init__.py`. + "ISC002", # ISC002 Implicitly concatenated string literals over multiple lines +] + +"setup.py" = ["PTH123"] + +"src/sphinx_exec_code/code_exec_error.py" = [ + "PERF401", # PERF401 Use a list comprehension to create a transformed list + "PLW2901", # PLW2901 `for` loop variable `tb_line` overwritten by assignment target +] + + +[lint.isort] +# https://docs.astral.sh/ruff/settings/#isort-lines-after-imports +lines-after-imports = 2 + + +[lint.pylint] +max-args = 6 diff --git a/doc/conf.py b/doc/conf.py index f6f8d50..84afc91 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -10,15 +10,18 @@ # 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 logging + import os import sys -# required for sphinx_exec_code + +# required for sphinx_exec_code to be found sys.path.insert(0, os.path.join(os.path.abspath('..'), 'src')) + import sphinx_exec_code + # -- Project information ----------------------------------------------------- project = 'sphinx-exec-code' copyright = '2021, spacemanspiff2007' diff --git a/requirements.txt b/requirements.txt index 40e8301..bfdba37 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,7 @@ -pytest >= 7.2, < 7.3 -pre-commit >= 3.0, < 3.1 +pytest == 7.4.4 +pre-commit == 3.5.0 -sphinx >= 6.1, < 6.2 -sphinx-rtd-theme >= 1.2, <1.3 +ruff == 0.1.11 + +sphinx == 7.2.6 +sphinx-rtd-theme == 2.0.0 diff --git a/setup.py b/setup.py index afcd1ae..d0f941a 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,7 @@ def load_version() -> str: package_dir={'': 'src'}, packages=find_packages('src', exclude=['tests*']), classifiers=[ - "Development Status :: 4 - Beta", + "Development Status :: 5 - Production/Stable", 'Intended Audience :: Developers', 'Framework :: Sphinx :: Extension', "License :: OSI Approved :: Apache Software License", @@ -56,6 +56,7 @@ def load_version() -> str: "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3 :: Only", ], ) diff --git a/src/sphinx_exec_code/__const__.py b/src/sphinx_exec_code/__const__.py index f9da61c..770383c 100644 --- a/src/sphinx_exec_code/__const__.py +++ b/src/sphinx_exec_code/__const__.py @@ -1,3 +1,4 @@ from sphinx.util import logging + log = logging.getLogger('sphinx-exec-code') diff --git a/src/sphinx_exec_code/__version__.py b/src/sphinx_exec_code/__version__.py index 969399e..438a041 100644 --- a/src/sphinx_exec_code/__version__.py +++ b/src/sphinx_exec_code/__version__.py @@ -1 +1 @@ -__version__ = '0.10' +__version__ = '0.11' diff --git a/src/sphinx_exec_code/code_exec.py b/src/sphinx_exec_code/code_exec.py index b2e9ac2..0b6251c 100644 --- a/src/sphinx_exec_code/code_exec.py +++ b/src/sphinx_exec_code/code_exec.py @@ -22,7 +22,7 @@ def execute_code(code: str, file: Path, first_loc: int) -> str: env['PYTHONPATH'] = os.pathsep.join(python_folders) run = subprocess.run([sys.executable, '-c', code.encode('utf-8')], capture_output=True, text=True, - encoding=encoding, cwd=cwd, env=env) + encoding=encoding, cwd=cwd, env=env, check=False) if run.returncode != 0: raise CodeExceptionError(code, file, first_loc, run.returncode, run.stderr) from None diff --git a/src/sphinx_exec_code/code_exec_error.py b/src/sphinx_exec_code/code_exec_error.py index 5ff4343..2bd4500 100644 --- a/src/sphinx_exec_code/code_exec_error.py +++ b/src/sphinx_exec_code/code_exec_error.py @@ -2,6 +2,7 @@ from pathlib import Path from typing import List + re_line = re.compile(r'^\s*File "()", line (\d+), in ', re.MULTILINE) diff --git a/src/sphinx_exec_code/code_format.py b/src/sphinx_exec_code/code_format.py index 94dcd22..697e97b 100644 --- a/src/sphinx_exec_code/code_format.py +++ b/src/sphinx_exec_code/code_format.py @@ -21,15 +21,15 @@ def __init__(self, marker: str): def is_marker(self, line: str) -> bool: if line == self.start: if not self.do_add: - raise VisibilityMarkerError(f'{self.start[1:]} already called! ' - f'Use {self.stop[1:]} or {self.toggle[1:]}!') + msg = f'{self.start[1:]} already called! Use {self.stop[1:]} or {self.toggle[1:]}!' + raise VisibilityMarkerError(msg) self.do_add = False return True if line == self.stop: if self.do_add: - raise VisibilityMarkerError(f'{self.stop[1:]} already called! ' - f'Use {self.start[1:]} or {self.toggle[1:]}!') + msg = f'{self.stop[1:]} already called! Use {self.start[1:]} or {self.toggle[1:]}!' + raise VisibilityMarkerError(msg) self.do_add = True self.skip_empty = True return True diff --git a/src/sphinx_exec_code/configuration/base.py b/src/sphinx_exec_code/configuration/base.py index d7672fd..854c971 100644 --- a/src/sphinx_exec_code/configuration/base.py +++ b/src/sphinx_exec_code/configuration/base.py @@ -5,6 +5,7 @@ from sphinx_exec_code.__const__ import log + TYPE_VALUE = TypeVar('TYPE_VALUE') @@ -18,7 +19,8 @@ def __init__(self, sphinx_name: str, initial_value: Optional[TYPE_VALUE] = None) @property def value(self) -> TYPE_VALUE: if self._value is None: - raise ConfigError(f'{self.sphinx_name} is not set!') + msg = f'{self.sphinx_name} is not set!' + raise ConfigError(msg) return self._value def transform_value(self, app: SphinxApp, value): @@ -40,5 +42,6 @@ def from_app(self, app: SphinxApp) -> TYPE_VALUE: self._value = self.validate_value(value) return self._value - def add_config_value(self, app: SphinxApp, sphinx_default): + def add_config_value(self, app: SphinxApp, sphinx_default: TYPE_VALUE): + self.validate_value(sphinx_default) app.add_config_value(self.sphinx_name, sphinx_default, 'env', self.SPHINX_TYPE) diff --git a/src/sphinx_exec_code/configuration/flag_config.py b/src/sphinx_exec_code/configuration/flag_config.py index fd075b7..2471e1c 100644 --- a/src/sphinx_exec_code/configuration/flag_config.py +++ b/src/sphinx_exec_code/configuration/flag_config.py @@ -6,5 +6,10 @@ class SphinxConfigFlag(SphinxConfigValue[bool]): SPHINX_TYPE = bool + def validate_value(self, value) -> bool: + if not isinstance(value, bool): + raise TypeError() + return value + def transform_value(self, app: SphinxApp, value) -> bool: return bool(value) diff --git a/src/sphinx_exec_code/configuration/path_config.py b/src/sphinx_exec_code/configuration/path_config.py index 31e9d6e..eb12d46 100644 --- a/src/sphinx_exec_code/configuration/path_config.py +++ b/src/sphinx_exec_code/configuration/path_config.py @@ -4,7 +4,7 @@ from sphinx.application import Sphinx as SphinxApp from sphinx_exec_code.__const__ import log -from sphinx_exec_code.configuration.base import SphinxConfigValue, TYPE_VALUE +from sphinx_exec_code.configuration.base import TYPE_VALUE, SphinxConfigValue class InvalidPathError(Exception): @@ -18,8 +18,9 @@ def make_path(self, app: SphinxApp, value) -> Path: try: path = Path(value) except Exception: - raise InvalidPathError(f'Could not create Path from "{value}" (type {type(value).__name__}) ' - f'(configured by {self.sphinx_name:s})') from None + msg = (f'Could not create Path from "{value}" (type {type(value).__name__}) ' + f'(configured by {self.sphinx_name:s})') + raise InvalidPathError(msg) from None if not path.is_absolute(): path = (Path(app.confdir) / path).resolve() @@ -27,7 +28,8 @@ def make_path(self, app: SphinxApp, value) -> Path: def check_folder_exists(self, folder: Path) -> Path: if not folder.is_dir(): - raise FileNotFoundError(f'Directory "{folder}" not found! (configured by {self.sphinx_name:s})') + msg = f'Directory "{folder}" not found! (configured by {self.sphinx_name:s})' + raise FileNotFoundError(msg) return folder diff --git a/src/sphinx_exec_code/configuration/values.py b/src/sphinx_exec_code/configuration/values.py index 2ebddad..16df932 100644 --- a/src/sphinx_exec_code/configuration/values.py +++ b/src/sphinx_exec_code/configuration/values.py @@ -1,6 +1,7 @@ from .flag_config import SphinxConfigFlag from .path_config import SphinxConfigFolder, SphinxConfigMultipleFolderStr + EXAMPLE_DIR = SphinxConfigFolder('exec_code_example_dir') # Options for code execution diff --git a/src/sphinx_exec_code/sphinx_api.py b/src/sphinx_exec_code/sphinx_api.py index f66370b..cd8c5f2 100644 --- a/src/sphinx_exec_code/sphinx_api.py +++ b/src/sphinx_exec_code/sphinx_api.py @@ -16,7 +16,6 @@ def builder_ready(app: SphinxApp): WORKING_DIR.from_app(app) PYTHONPATH_FOLDERS.from_app(app) SET_UTF8_ENCODING.from_app(app) - return None def setup(app): @@ -33,7 +32,7 @@ def setup(app): EXAMPLE_DIR.add_config_value(app, confdir) WORKING_DIR.add_config_value(app, confdir.parent) PYTHONPATH_FOLDERS.add_config_value(app, code_folders) - SET_UTF8_ENCODING.add_config_value(app, True if os.name == 'nt' else False) # Somehow this workaround is required + SET_UTF8_ENCODING.add_config_value(app, os.name == 'nt') app.connect('builder-inited', builder_ready) app.add_directive('exec_code', ExecCode) diff --git a/src/sphinx_exec_code/sphinx_exec.py b/src/sphinx_exec_code/sphinx_exec.py index a425e23..9d4c696 100644 --- a/src/sphinx_exec_code/sphinx_exec.py +++ b/src/sphinx_exec_code/sphinx_exec.py @@ -8,9 +8,9 @@ from sphinx_exec_code.__const__ import log from sphinx_exec_code.code_exec import CodeExceptionError, execute_code -from sphinx_exec_code.code_format import get_show_exec_code, VisibilityMarkerError +from sphinx_exec_code.code_format import VisibilityMarkerError, get_show_exec_code from sphinx_exec_code.configuration import EXAMPLE_DIR -from sphinx_exec_code.sphinx_spec import build_spec, SpecCode, SpecOutput, SphinxSpecBase +from sphinx_exec_code.sphinx_spec import SpecCode, SpecOutput, SphinxSpecBase, build_spec def create_literal_block(objs: list, code: str, spec: SphinxSpecBase): @@ -45,7 +45,7 @@ def run(self) -> list: try: return self._run() except ExtensionError as e: - raise e + raise e from None except Exception as e: name = self.__class__.__name__ log.error(f'Error while running {name}!') @@ -53,7 +53,9 @@ def run(self) -> list: if not isinstance(e, VisibilityMarkerError): for line in traceback.format_exc().splitlines(): log.error(line) - raise ExtensionError(f'Error while running {name}!', orig_exc=e) + + msg = f'Error while running {name}!' + raise ExtensionError(msg, orig_exc=e) from None def _get_code_line(self, line_no: int, content: List[str]) -> int: """Get the first line number of the code""" @@ -63,7 +65,7 @@ def _get_code_line(self, line_no: int, content: List[str]) -> int: i = 0 first_line = content[0] - for i, raw_line in enumerate(self.block_text.splitlines()): # noqa: B007 + for i, raw_line in enumerate(self.block_text.splitlines()): # raw line contains the leading white spaces if raw_line.lstrip() == first_line: break @@ -93,7 +95,8 @@ def _run(self) -> list: try: code_show, code_exec = get_show_exec_code(content) except Exception as e: - raise ExtensionError(f'Could not parse code markers at {self.get_location()}', orig_exc=e) + msg = f'Could not parse code markers at {self.get_location()}' + raise ExtensionError(msg, orig_exc=e) from None # Show the code from the user create_literal_block(output, code_show, spec=code_spec) @@ -108,7 +111,8 @@ def _run(self) -> list: for line in e.pformat(): log.error(line) - raise ExtensionError('Could not execute code!') from None + msg = 'Could not execute code!' + raise ExtensionError(msg) from None # Show the output from the code execution create_literal_block(output, code_results, spec=SpecOutput.from_options(self.options)) diff --git a/src/sphinx_exec_code/sphinx_spec.py b/src/sphinx_exec_code/sphinx_spec.py index 0bbbd9a..2218d8d 100644 --- a/src/sphinx_exec_code/sphinx_spec.py +++ b/src/sphinx_exec_code/sphinx_spec.py @@ -1,11 +1,11 @@ -from typing import Any, Callable, Dict +from typing import Any, Callable, ClassVar, Dict from docutils.parsers.rst import directives # type: ignore class SphinxSpecBase: - aliases: Dict[str, str] - defaults: Dict[str, str] + aliases: ClassVar[Dict[str, str]] + defaults: ClassVar[Dict[str, str]] def __init__(self, hide: bool, linenos: bool, caption: str, language: str): # flags @@ -45,14 +45,14 @@ def build_spec() -> Dict[str, Callable[[Any], Any]]: class SpecCode(SphinxSpecBase): - aliases = { + aliases: ClassVar = { 'hide_code': 'hide', 'caption': 'caption', 'language': 'language', 'linenos': 'linenos', 'filename': 'filename', } - defaults = { + defaults: ClassVar = { 'caption': '', 'language': 'python', 'filename': '', @@ -64,13 +64,13 @@ def __init__(self, hide: bool, linenos: bool, caption: str, language: str, filen class SpecOutput(SphinxSpecBase): - aliases = { + aliases: ClassVar = { 'hide_output': 'hide', 'caption_output': 'caption', 'language_output': 'language', 'linenos_output': 'linenos', } - defaults = { + defaults: ClassVar = { 'caption': '', 'language': 'none', } diff --git a/tests/test_code_exec.py b/tests/test_code_exec.py index 31a16f7..f7cdbf8 100644 --- a/tests/test_code_exec.py +++ b/tests/test_code_exec.py @@ -8,15 +8,15 @@ @pytest.fixture() -def setup_env(monkeypatch): +def _setup_env(monkeypatch): f = Path(__file__).parent monkeypatch.setattr(WORKING_DIR, '_value', f) monkeypatch.setattr(PYTHONPATH_FOLDERS, '_value', [str(f)]) - return None @pytest.mark.parametrize('utf8', [True, False]) -def test_print(setup_env, monkeypatch, utf8): +@pytest.mark.usefixtures('_setup_env') +def test_print(monkeypatch, utf8): monkeypatch.setattr(SET_UTF8_ENCODING, '_value', utf8) code = "print('Line1')\nprint('Line2')" @@ -25,7 +25,8 @@ def test_print(setup_env, monkeypatch, utf8): @pytest.mark.parametrize('utf8', [True, False]) -def test_print_table(setup_env, monkeypatch, utf8): +@pytest.mark.usefixtures('_setup_env') +def test_print_table(monkeypatch, utf8): monkeypatch.setattr(SET_UTF8_ENCODING, '_value', utf8) code = "\n \n \n\n" \ @@ -36,7 +37,8 @@ def test_print_table(setup_env, monkeypatch, utf8): @pytest.mark.parametrize('utf8', [True, False]) -def test_err(setup_env, monkeypatch, utf8): +@pytest.mark.usefixtures('_setup_env') +def test_err(monkeypatch, utf8): monkeypatch.setattr(SET_UTF8_ENCODING, '_value', utf8) code = "print('Line1')\nprint('Line2')\n1/0" @@ -60,7 +62,8 @@ def test_err(setup_env, monkeypatch, utf8): @pytest.mark.skipif(not IS_WIN, reason='Windows only') -def test_unicode_fails(setup_env, monkeypatch): +@pytest.mark.usefixtures('_setup_env') +def test_unicode_fails(monkeypatch): code = "print('●')" monkeypatch.setattr(SET_UTF8_ENCODING, '_value', False) @@ -69,9 +72,13 @@ def test_unicode_fails(setup_env, monkeypatch): monkeypatch.setattr(SET_UTF8_ENCODING, '_value', True) assert execute_code(code, 'my_file', 1) == '●' + code = "print('This is a beautiful unicode character: \u265E.')" + assert execute_code(code, 'my_file', 1) == 'This is a beautiful unicode character: \u265E.' + @pytest.mark.skipif(IS_WIN, reason='Fails on Windows') -def test_unicode_no_utf8(setup_env, monkeypatch): +@pytest.mark.usefixtures('_setup_env') +def test_unicode_no_utf8(monkeypatch): code = "print('●')" monkeypatch.setattr(SET_UTF8_ENCODING, '_value', False) diff --git a/tests/test_code_format.py b/tests/test_code_format.py index a690f69..659a89f 100644 --- a/tests/test_code_format.py +++ b/tests/test_code_format.py @@ -1,6 +1,6 @@ import pytest -from sphinx_exec_code.code_format import get_show_exec_code, VisibilityMarkerError +from sphinx_exec_code.code_format import VisibilityMarkerError, get_show_exec_code def test_format_hide(): diff --git a/tests/test_sphinx_spec.py b/tests/test_sphinx_spec.py index 0d3cf2d..c2ec7bf 100644 --- a/tests/test_sphinx_spec.py +++ b/tests/test_sphinx_spec.py @@ -4,7 +4,7 @@ import pytest from docutils.parsers.rst import directives -from sphinx_exec_code.sphinx_spec import build_spec, SpecCode, SpecOutput, SphinxSpecBase +from sphinx_exec_code.sphinx_spec import SpecCode, SpecOutput, SphinxSpecBase, build_spec def test_aliases_unique(): @@ -14,7 +14,7 @@ def test_aliases_unique(): assert key_output not in SpecCode.aliases -@pytest.mark.parametrize('cls', (SpecCode, SpecOutput)) +@pytest.mark.parametrize('cls', [SpecCode, SpecOutput]) def test_default_in_aliases(cls: Type[SphinxSpecBase]): names = list(cls.aliases.values()) for k in cls.defaults: