From 1c46b7cfaeb0eff1bb178f075f4bf47de82dede0 Mon Sep 17 00:00:00 2001 From: spacemanspiff2007 <10754716+spacemanspiff2007@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:54:15 +0200 Subject: [PATCH] Update to Python 3.13 (#13) --- .github/workflows/pre-commit.yml | 4 +- .github/workflows/publish-pypi.yml | 4 +- .github/workflows/run_tox.yml | 6 +- .pre-commit-config.yaml | 23 +++- .ruff.toml | 133 +++++++++++---------- requirements.txt | 17 ++- setup.py | 35 +++--- src/sphinx_exec_code/__init__.py | 6 +- src/sphinx_exec_code/__version__.py | 2 +- src/sphinx_exec_code/code_exec_error.py | 2 +- src/sphinx_exec_code/code_format.py | 4 +- src/sphinx_exec_code/configuration/base.py | 6 +- src/sphinx_exec_code/sphinx_api.py | 5 +- src/sphinx_exec_code/sphinx_exec.py | 2 +- src/sphinx_exec_code/sphinx_spec.py | 6 +- tests/test_code_exec.py | 43 ++++++- tests/test_code_format.py | 18 +-- tests/test_sphinx_config.py | 2 +- tests/test_sphinx_spec.py | 12 +- tox.ini | 7 +- 20 files changed, 203 insertions(+), 134 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index f480529..6177f25 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -11,5 +11,5 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.10' - - uses: pre-commit/action@v3.0.0 + python-version: '3.12' + - uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 02d9f1c..0e2190f 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -16,10 +16,10 @@ jobs: - uses: actions/checkout@v4 with: ref: main - - name: Set up Python 3.10 + - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.12' - name: Install setuptools run: | diff --git a/.github/workflows/run_tox.yml b/.github/workflows/run_tox.yml index 78187e0..1407961 100644 --- a/.github/workflows/run_tox.yml +++ b/.github/workflows/run_tox.yml @@ -10,8 +10,8 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.10' - - uses: pre-commit/action@v3.0.0 + python-version: '3.12' + - uses: pre-commit/action@v3.0.1 test: needs: pre-commit @@ -19,7 +19,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] steps: - uses: actions/checkout@v4 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9a40a02..f8a152a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v5.0.0 hooks: - id: check-ast - id: check-builtin-literals @@ -13,9 +13,28 @@ repos: - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.11 # check that this version matches the requirements.txt + rev: v0.6.9 hooks: - id: ruff + name: ruff unused imports + # F401 [*] {name} imported but unused + args: [ "--select", "F401", "--extend-exclude", "__init__.py", "--fix"] + + - id: ruff + # I001 [*] Import block is un-sorted or un-formatted + # UP035 [*] Import from {target} instead: {names} + # Q000 [*] Double quote found but single quotes preferred + # Q001 [*] Double quote multiline found but single quotes preferred + args: [ "--select", "I001,UP035,Q000,Q001", "--fix"] + + + - repo: https://github.com/JelleZijlstra/autotyping + rev: 24.9.0 + hooks: + - id: autotyping + types: [python] + args: [--safe] + - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 diff --git a/.ruff.toml b/.ruff.toml index d9d9603..ef366af 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -1,85 +1,96 @@ - -line-length = 120 indent-width = 4 +line-length = 120 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 + +src = [ + "src", + "tests" ] + +[lint] +select = ["ALL"] + ignore = [ + "D", # https://docs.astral.sh/ruff/rules/#pydocstyle-d + "T20", # https://docs.astral.sh/ruff/rules/#flake8-print-t20 + "DTZ", # https://docs.astral.sh/ruff/rules/#flake8-datetimez-dtz + "SLF", # https://docs.astral.sh/ruff/rules/#flake8-self-slf + "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/ + # https://docs.astral.sh/ruff/rules/#flake8-builtins-a + "A003", # Python builtin is shadowed by class attribute {name} from {row} + + # https://docs.astral.sh/ruff/rules/#pyflakes-f + "F401", # {name} imported but unused; consider using importlib.util.find_spec to test for availability + + # https://docs.astral.sh/ruff/rules/#flake8-bandit-s + "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes + + # https://docs.astral.sh/ruff/rules/#pyupgrade-up + "UP038", # Use X | Y in {} call instead of (X, Y) + + # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann + "ANN101", # Missing type annotation for {name} in method + "ANN102", # Missing type annotation for {name} in classmethod + "ANN401", # Dynamically typed expressions (typing.Any) are disallowed in {name} + + # https://docs.astral.sh/ruff/rules/#flake8-blind-except-ble + "BLE001", # Do not catch blind exception: {name} + + # https://docs.astral.sh/ruff/rules/#flake8-raise-rse + "RSE102", # Unnecessary parentheses on raised exception + + # https://docs.astral.sh/ruff/rules/#flake8-commas-com + "COM812", # Trailing comma missing + "COM819", # Trailing comma prohibited + + # https://docs.astral.sh/ruff/rules/#warning-w_1 + "PLW0603", # Using the global statement to update {name} is discouraged + + # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g + "G004", # Logging statement uses f-string + + # https://docs.astral.sh/ruff/rules/#refactor-r + "PLR1711", # Useless return statement at end of function + + # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf + "RUF005", # Consider {expression} instead of concatenation ] [format] -# Use single quotes for non-triple-quoted strings. quote-style = "single" +# https://docs.astral.sh/ruff/settings/#lintflake8-quotes +[lint.flake8-quotes] +inline-quotes = "single" +multiline-quotes = "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 -] +[lint.flake8-builtins] +builtins-ignorelist = ["id", "input"] -"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 -] +# https://docs.astral.sh/ruff/settings/#lintisort +[lint.isort] +lines-after-imports = 2 # https://docs.astral.sh/ruff/settings/#lint_isort_lines-after-imports -[lint.isort] -# https://docs.astral.sh/ruff/settings/#isort-lines-after-imports -lines-after-imports = 2 +[lint.per-file-ignores] +"setup.py" = ["PTH123"] +"tests/*" = [ + "ANN", # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann + + # https://docs.astral.sh/ruff/rules/#flake8-bandit-s + "S101", # Use of assert detected -[lint.pylint] -max-args = 6 + # https://docs.astral.sh/ruff/rules/#refactor-r + "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable + "PLR0913", # Too many arguments in function definition ({c_args} > {max_args}) +] diff --git a/requirements.txt b/requirements.txt index bf4222d..5b3ab96 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,14 @@ -pytest == 7.4.4 -pre-commit == 3.5.0 +# Dependencies for old python versions +pre-commit == 3.5.0; python_version < '3.9' +pytest == 7.4.4; python_version < '3.10' +sphinx == 7.4.7; python_version == '3.9' +sphinx == 7.1.2; python_version == '3.8' -ruff == 0.1.11 +# Current dependencies +pre-commit == 4.0.1; python_version >= '3.9' +pytest == 8.3.3; python_version >= '3.10' +sphinx == 8.1.3; python_version >= '3.10' -sphinx == 7.1.2 -sphinx-rtd-theme == 2.0.0 +ruff == 0.6.9 + +sphinx-rtd-theme == 3.0.1 diff --git a/setup.py b/setup.py index d0f941a..8a68664 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ def load_version() -> str: version: typing.Dict[str, str] = {} - with open("src/sphinx_exec_code/__version__.py") as fp: + with open('src/sphinx_exec_code/__version__.py') as fp: exec(fp.read(), version) assert version['__version__'], version return version['__version__'] @@ -21,15 +21,15 @@ def load_version() -> str: readme = Path(__file__).with_name('readme.md') long_description = '' if readme.is_file(): - with readme.open("r", encoding='utf-8') as fh: + with readme.open('r', encoding='utf-8') as fh: long_description = fh.read() setup( - name="sphinx-exec-code", + name='sphinx-exec-code', version=__version__, - author="spaceman_spiff", + author='spaceman_spiff', # author_email="", - description="Execute code blocks in Sphinx and display the output", + description='Execute code blocks in Sphinx and display the output', keywords=[ 'sphinx', 'execute', @@ -37,8 +37,8 @@ def load_version() -> str: 'code' ], long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/spacemanspiff2007/sphinx-exec-code", + long_description_content_type='text/markdown', + url='https://github.com/spacemanspiff2007/sphinx-exec-code', project_urls={ 'GitHub': 'https://github.com/spacemanspiff2007/sphinx-exec-code', 'Documentation': 'https://sphinx-exec-code.readthedocs.io/', @@ -46,17 +46,18 @@ def load_version() -> str: package_dir={'': 'src'}, packages=find_packages('src', exclude=['tests*']), classifiers=[ - "Development Status :: 5 - Production/Stable", + 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Framework :: Sphinx :: Extension', - "License :: OSI Approved :: Apache Software License", - "Natural Language :: English", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3.8", - "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", + 'License :: OSI Approved :: Apache Software License', + 'Natural Language :: English', + 'Operating System :: OS Independent', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', + 'Programming Language :: Python :: 3 :: Only', ], ) diff --git a/src/sphinx_exec_code/__init__.py b/src/sphinx_exec_code/__init__.py index a0c7577..544e7c2 100644 --- a/src/sphinx_exec_code/__init__.py +++ b/src/sphinx_exec_code/__init__.py @@ -1,5 +1,3 @@ -from .__version__ import __version__ - -# isort: split - from sphinx_exec_code.sphinx_api import setup + +from .__version__ import __version__ diff --git a/src/sphinx_exec_code/__version__.py b/src/sphinx_exec_code/__version__.py index b804441..6d41a8f 100644 --- a/src/sphinx_exec_code/__version__.py +++ b/src/sphinx_exec_code/__version__.py @@ -1 +1 @@ -__version__ = '0.12' +__version__ = '0.13' diff --git a/src/sphinx_exec_code/code_exec_error.py b/src/sphinx_exec_code/code_exec_error.py index 2bd4500..ed7f38d 100644 --- a/src/sphinx_exec_code/code_exec_error.py +++ b/src/sphinx_exec_code/code_exec_error.py @@ -7,7 +7,7 @@ class CodeExceptionError(Exception): - def __init__(self, code: str, file: Path, first_loc: int, ret: int, stderr: str): + def __init__(self, code: str, file: Path, first_loc: int, ret: int, stderr: str) -> None: self.code = code self.file: Path = file diff --git a/src/sphinx_exec_code/code_format.py b/src/sphinx_exec_code/code_format.py index 697e97b..42e5c60 100644 --- a/src/sphinx_exec_code/code_format.py +++ b/src/sphinx_exec_code/code_format.py @@ -8,7 +8,7 @@ class VisibilityMarkerError(Exception): class CodeMarker: MARKERS = ('hide', 'skip') - def __init__(self, marker: str): + def __init__(self, marker: str) -> None: assert marker in CodeMarker.MARKERS self.start = f'#{marker}:start' self.stop = f'#{marker}:stop' @@ -40,7 +40,7 @@ def is_marker(self, line: str) -> bool: return False - def add_line(self, line: str): + def add_line(self, line: str) -> None: if not self.do_add: return None diff --git a/src/sphinx_exec_code/configuration/base.py b/src/sphinx_exec_code/configuration/base.py index 854c971..717e578 100644 --- a/src/sphinx_exec_code/configuration/base.py +++ b/src/sphinx_exec_code/configuration/base.py @@ -12,7 +12,7 @@ class SphinxConfigValue(Generic[TYPE_VALUE]): SPHINX_TYPE: Union[Tuple[Type[Any], ...], Type[Any]] - def __init__(self, sphinx_name: str, initial_value: Optional[TYPE_VALUE] = None): + def __init__(self, sphinx_name: str, initial_value: Optional[TYPE_VALUE] = None) -> None: self.sphinx_name: Final = sphinx_name self._value: Optional[TYPE_VALUE] = initial_value @@ -23,7 +23,7 @@ def value(self) -> TYPE_VALUE: raise ConfigError(msg) return self._value - def transform_value(self, app: SphinxApp, value): + def transform_value(self, app: SphinxApp, value: Any) -> TYPE_VALUE: return value def validate_value(self, value) -> TYPE_VALUE: @@ -42,6 +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: TYPE_VALUE): + def add_config_value(self, app: SphinxApp, sphinx_default: TYPE_VALUE) -> None: 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/sphinx_api.py b/src/sphinx_exec_code/sphinx_api.py index cd8c5f2..fb13f0d 100644 --- a/src/sphinx_exec_code/sphinx_api.py +++ b/src/sphinx_exec_code/sphinx_api.py @@ -1,5 +1,6 @@ import os from pathlib import Path +from typing import Any, Dict from sphinx.application import Sphinx as SphinxApp @@ -10,7 +11,7 @@ from .configuration import EXAMPLE_DIR, PYTHONPATH_FOLDERS, SET_UTF8_ENCODING, WORKING_DIR -def builder_ready(app: SphinxApp): +def builder_ready(app: SphinxApp) -> None: # load configuration EXAMPLE_DIR.from_app(app) WORKING_DIR.from_app(app) @@ -18,7 +19,7 @@ def builder_ready(app: SphinxApp): SET_UTF8_ENCODING.from_app(app) -def setup(app): +def setup(app) -> Dict[str, Any]: """ Register sphinx_execute_code directive with Sphinx """ confdir = Path(app.confdir) diff --git a/src/sphinx_exec_code/sphinx_exec.py b/src/sphinx_exec_code/sphinx_exec.py index b1017c1..23b000e 100644 --- a/src/sphinx_exec_code/sphinx_exec.py +++ b/src/sphinx_exec_code/sphinx_exec.py @@ -13,7 +13,7 @@ from sphinx_exec_code.sphinx_spec import SphinxSpecBase, build_spec, get_specs -def create_literal_block(objs: list, code: str, spec: SphinxSpecBase): +def create_literal_block(objs: list, code: str, spec: SphinxSpecBase) -> None: if spec.hide or not code: return None diff --git a/src/sphinx_exec_code/sphinx_spec.py b/src/sphinx_exec_code/sphinx_spec.py index 33b9ecc..b68c85c 100644 --- a/src/sphinx_exec_code/sphinx_spec.py +++ b/src/sphinx_exec_code/sphinx_spec.py @@ -7,7 +7,7 @@ class SphinxSpecBase: aliases: ClassVar[Dict[str, str]] defaults: ClassVar[Dict[str, str]] - def __init__(self, hide: bool, linenos: bool, caption: str, language: str): + def __init__(self, hide: bool, linenos: bool, caption: str, language: str) -> None: # flags self.hide = hide self.linenos = linenos @@ -32,7 +32,7 @@ def from_options(cls, options: Dict[str, Any]) -> 'SphinxSpecBase': return cls(**opts) @classmethod - def update_spec(cls, spec: Dict[str, Callable[[Any], Any]]): + def update_spec(cls, spec: Dict[str, Callable[[Any], Any]]) -> None: for alias, name in cls.aliases.items(): # Flags don't have a default spec[alias] = directives.flag if name not in cls.defaults else directives.unchanged @@ -73,7 +73,7 @@ class SpecCode(SphinxSpecBase): 'filename': '', } - def __init__(self, hide: bool, linenos: bool, caption: str, language: str, filename: str): + def __init__(self, hide: bool, linenos: bool, caption: str, language: str, filename: str) -> None: super().__init__(hide, linenos, caption, language) self.filename: str = filename diff --git a/tests/test_code_exec.py b/tests/test_code_exec.py index f7cdbf8..a90ba10 100644 --- a/tests/test_code_exec.py +++ b/tests/test_code_exec.py @@ -1,4 +1,5 @@ import os +import sys from pathlib import Path import pytest @@ -8,7 +9,7 @@ @pytest.fixture() -def _setup_env(monkeypatch): +def _setup_env(monkeypatch) -> None: f = Path(__file__).parent monkeypatch.setattr(WORKING_DIR, '_value', f) monkeypatch.setattr(PYTHONPATH_FOLDERS, '_value', [str(f)]) @@ -16,7 +17,7 @@ def _setup_env(monkeypatch): @pytest.mark.parametrize('utf8', [True, False]) @pytest.mark.usefixtures('_setup_env') -def test_print(monkeypatch, utf8): +def test_print(monkeypatch, utf8) -> None: monkeypatch.setattr(SET_UTF8_ENCODING, '_value', utf8) code = "print('Line1')\nprint('Line2')" @@ -26,7 +27,7 @@ def test_print(monkeypatch, utf8): @pytest.mark.parametrize('utf8', [True, False]) @pytest.mark.usefixtures('_setup_env') -def test_print_table(monkeypatch, utf8): +def test_print_table(monkeypatch, utf8) -> None: monkeypatch.setattr(SET_UTF8_ENCODING, '_value', utf8) code = "\n \n \n\n" \ @@ -36,9 +37,36 @@ def test_print_table(monkeypatch, utf8): assert output == ' | A | B |\n Col1 | 1 | 2 |' +PYTHON_3_13 = sys.version_info[:2] >= (3, 13) + + +@pytest.mark.skipif(PYTHON_3_13, reason='Old traceback') +@pytest.mark.parametrize('utf8', [True, False]) +@pytest.mark.usefixtures('_setup_env') +def test_err_12(monkeypatch, utf8) -> None: + monkeypatch.setattr(SET_UTF8_ENCODING, '_value', utf8) + + code = "print('Line1')\nprint('Line2')\n1/0" + + with pytest.raises(CodeExceptionError) as e: + execute_code(code, Path('/my_file'), 5) + + msg = e.value.pformat() + assert msg == [ + " print('Line1')", + " print('Line2')", + ' 1/0 <--', + '', + 'Traceback (most recent call last):', + ' File "my_file", line 7', + 'ZeroDivisionError: division by zero' + ] + + +@pytest.mark.skipif(not PYTHON_3_13, reason='Old traceback') @pytest.mark.parametrize('utf8', [True, False]) @pytest.mark.usefixtures('_setup_env') -def test_err(monkeypatch, utf8): +def test_err_13(monkeypatch, utf8) -> None: monkeypatch.setattr(SET_UTF8_ENCODING, '_value', utf8) code = "print('Line1')\nprint('Line2')\n1/0" @@ -47,6 +75,7 @@ def test_err(monkeypatch, utf8): execute_code(code, Path('/my_file'), 5) msg = e.value.pformat() + assert msg == [ " print('Line1')", " print('Line2')", @@ -54,6 +83,8 @@ def test_err(monkeypatch, utf8): '', 'Traceback (most recent call last):', ' File "my_file", line 7', + ' 1/0', + ' ~^~', 'ZeroDivisionError: division by zero' ] @@ -63,7 +94,7 @@ def test_err(monkeypatch, utf8): @pytest.mark.skipif(not IS_WIN, reason='Windows only') @pytest.mark.usefixtures('_setup_env') -def test_unicode_fails(monkeypatch): +def test_unicode_fails(monkeypatch) -> None: code = "print('●')" monkeypatch.setattr(SET_UTF8_ENCODING, '_value', False) @@ -78,7 +109,7 @@ def test_unicode_fails(monkeypatch): @pytest.mark.skipif(IS_WIN, reason='Fails on Windows') @pytest.mark.usefixtures('_setup_env') -def test_unicode_no_utf8(monkeypatch): +def test_unicode_no_utf8(monkeypatch) -> None: code = "print('●')" monkeypatch.setattr(SET_UTF8_ENCODING, '_value', False) diff --git a/tests/test_code_format.py b/tests/test_code_format.py index 659a89f..05edb39 100644 --- a/tests/test_code_format.py +++ b/tests/test_code_format.py @@ -3,35 +3,35 @@ from sphinx_exec_code.code_format import VisibilityMarkerError, get_show_exec_code -def test_format_hide(): +def test_format_hide() -> None: code = 'print("1")\n# - hide: start - \nprint("2")\n #hide:stop\n \n \nprint("3")' show, run = get_show_exec_code(code.splitlines()) assert show == 'print("1")\nprint("3")' assert run == 'print("1")\nprint("2")\n\n\nprint("3")' -def test_format_skip(): +def test_format_skip() -> None: code = 'print("1")\n# - skip: start - \nprint("2")\n #skip:stop\nprint("3")' show, run = get_show_exec_code(code.splitlines()) assert show == 'print("1")\nprint("2")\nprint("3")' assert run == 'print("1")\nprint("3")' -def test_marker_err(): +def test_marker_err() -> None: code = 'print("1")\n# - hide: start - \n# - hide: start - \nprint("2")\n #hide:stop\nprint("3")' with pytest.raises(VisibilityMarkerError): get_show_exec_code(code.splitlines()) -def test_code_indent(): - code = """ +def test_code_indent() -> None: + code = ''' print('asdf') print('1234') # comment - """ + ''' show, run = get_show_exec_code(code.splitlines()) assert show == "print('asdf')\n" \ @@ -39,20 +39,20 @@ def test_code_indent(): " # comment" -def test_code_split_empty(): +def test_code_split_empty() -> None: show, run = get_show_exec_code(['']) assert show == '' assert run == '' -def test_code_no_show(): +def test_code_no_show() -> None: code = '# - hide: start -\nprint("l1")\nprint("l2")' show, run = get_show_exec_code(code.splitlines()) assert show == '' assert run == 'print("l1")\nprint("l2")' -def test_code_no_exec(): +def test_code_no_exec() -> None: code = '# - skip: start -\nprint(1 / 0)\nprint(2 / 0)' show, run = get_show_exec_code(code.splitlines()) assert show == 'print(1 / 0)\nprint(2 / 0)' diff --git a/tests/test_sphinx_config.py b/tests/test_sphinx_config.py index 4b7827f..2825edd 100644 --- a/tests/test_sphinx_config.py +++ b/tests/test_sphinx_config.py @@ -6,7 +6,7 @@ from sphinx_exec_code.configuration.values import SphinxConfigFolder -def test_path_errors(): +def test_path_errors() -> None: a = SphinxConfigFolder('config_key_name') with pytest.raises(FileNotFoundError) as e: diff --git a/tests/test_sphinx_spec.py b/tests/test_sphinx_spec.py index 51ad00b..6297848 100644 --- a/tests/test_sphinx_spec.py +++ b/tests/test_sphinx_spec.py @@ -7,7 +7,7 @@ from sphinx_exec_code.sphinx_spec import SpecCode, SpecOutput, SphinxSpecBase, build_spec, get_specs -def test_aliases_unique(): +def test_aliases_unique() -> None: for key_code in SpecCode.aliases: assert key_code not in SpecOutput.aliases for key_output in SpecOutput.aliases: @@ -15,13 +15,13 @@ def test_aliases_unique(): @pytest.mark.parametrize('cls', [SpecCode, SpecOutput]) -def test_default_in_aliases(cls: Type[SphinxSpecBase]): +def test_default_in_aliases(cls: Type[SphinxSpecBase]) -> None: names = list(cls.aliases.values()) for k in cls.defaults: assert k in names -def test_build_spec_code(): +def test_build_spec_code() -> None: spec = build_spec() for name in chain(SpecCode.aliases.keys(), SpecOutput.aliases.keys()): @@ -41,7 +41,7 @@ def test_build_spec_code(): } -def test_spec_code(): +def test_spec_code() -> None: obj = SpecCode.from_options({'linenos': None, 'caption': 'my_header', 'filename': 'filename'}) assert obj.caption == 'my_header' assert obj.language == 'python' @@ -50,7 +50,7 @@ def test_spec_code(): assert obj.filename == 'filename' -def test_spec_output(): +def test_spec_output() -> None: obj = SpecOutput.from_options({'hide_output': None, 'caption_output': 'my_header_out'}) assert obj.caption == 'my_header_out' assert obj.language == 'none' @@ -58,7 +58,7 @@ def test_spec_output(): assert obj.hide is True -def test_invalid_options(): +def test_invalid_options() -> None: with pytest.raises(ValueError) as e: # noqa: PT011 get_specs({'hide-output': None}) diff --git a/tox.ini b/tox.ini index b4c5442..d5c8c95 100644 --- a/tox.ini +++ b/tox.ini @@ -1,21 +1,22 @@ # content of: tox.ini , put in same dir as setup.py [tox] envlist = - py37 py38 py39 py310 py311 + py312 + py313 flake docs [gh-actions] python = - 3.7: py37 3.8: py38 3.9: py39 - 3.10: py310, flake, docs + 3.10: py310 3.11: py311 + 3.12: py312, flake, docs [testenv] deps =