From aeef80daee9c61517a2bcbe29c07028e123d6f02 Mon Sep 17 00:00:00 2001 From: Henri Hulski Date: Mon, 10 Aug 2026 18:38:54 +0200 Subject: [PATCH 1/7] test: add tests for 100% coverage --- doc/_build/doctest/output.txt | 9 ++ .../bowerstatic_objects.inv | Bin 0 -> 249 bytes .../__intersphinx_cache__/reg_objects.inv | Bin 0 -> 523 bytes doc/_build/doctrees/api.doctree | Bin 0 -> 29234 bytes doc/_build/doctrees/changes.doctree | Bin 0 -> 8051 bytes doc/_build/doctrees/developing.doctree | Bin 0 -> 19053 bytes doc/_build/doctrees/environment.pickle | Bin 0 -> 28297 bytes doc/_build/doctrees/index.doctree | Bin 0 -> 5102 bytes .../fixtures/importerror_pkg/__init__.py | 1 + .../fixtures/importerror_pkg/sub/__init__.py | 1 + .../importerror_pkg_handle_error/__init__.py | 1 + .../sub/__init__.py | 1 + importscan/tests/test_importscan.py | 86 ++++++++++++++++++ 13 files changed, 99 insertions(+) create mode 100644 doc/_build/doctest/output.txt create mode 100644 doc/_build/doctrees/__intersphinx_cache__/bowerstatic_objects.inv create mode 100644 doc/_build/doctrees/__intersphinx_cache__/reg_objects.inv create mode 100644 doc/_build/doctrees/api.doctree create mode 100644 doc/_build/doctrees/changes.doctree create mode 100644 doc/_build/doctrees/developing.doctree create mode 100644 doc/_build/doctrees/environment.pickle create mode 100644 doc/_build/doctrees/index.doctree create mode 100644 importscan/tests/fixtures/importerror_pkg/__init__.py create mode 100644 importscan/tests/fixtures/importerror_pkg/sub/__init__.py create mode 100644 importscan/tests/fixtures/importerror_pkg_handle_error/__init__.py create mode 100644 importscan/tests/fixtures/importerror_pkg_handle_error/sub/__init__.py diff --git a/doc/_build/doctest/output.txt b/doc/_build/doctest/output.txt new file mode 100644 index 0000000..358007f --- /dev/null +++ b/doc/_build/doctest/output.txt @@ -0,0 +1,9 @@ +Results of doctest builder run on 2026-08-07 09:26:02 +===================================================== + +Doctest summary +=============== + 0 tests + 0 failures in tests + 0 failures in setup code + 0 failures in cleanup code diff --git a/doc/_build/doctrees/__intersphinx_cache__/bowerstatic_objects.inv b/doc/_build/doctrees/__intersphinx_cache__/bowerstatic_objects.inv new file mode 100644 index 0000000000000000000000000000000000000000..bad868b1240800e6f88bec0f53557ad2901b8c4b GIT binary patch literal 249 zcmY#Z2rkIT%&Sny%qvUHE6FdaR47X=D$dN$Q!wIERtPA{&q_@$u~Kl#FHbEBE=erO zOa=;vL6lf280Z-q=%u8V82|-CGEx zQEG8ab{k+LRC&?k{(yZEpI=cb0^MthimA0Zan4b<8?Ohf@#Q!GuocM`d&JW zUN2IoJb9+ETEoKmO5xI{;!CG36%!0ue|qY?S<`1upA^-&N<@t-aQOqyq~fQ~mL~*8 u1+3(r+_37~S%1y5o~>D0e!eNERX>N99Zgg*Qc_4OWa&u{KZXhxWBOp+6Z)#;@bUGkXWoHT_ zAXa5^b7^mGIv_AEF)}V>Wp*$MBOp|0Wgv28ZDDC{WMy(7Z)PBLXlZjGW@&6?AZc?T zV{dJ6a%FRKWn>_Ab7^j8AbMi7sSE3&&qnz~g N*U2La{{juvsM(0F?L`0p literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/api.doctree b/doc/_build/doctrees/api.doctree new file mode 100644 index 0000000000000000000000000000000000000000..f7a4b53b6be6f00028ffd70b1f7db665fb07f259 GIT binary patch literal 29234 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 EA0Z5VhX4Qo literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/changes.doctree b/doc/_build/doctrees/changes.doctree new file mode 100644 index 0000000000000000000000000000000000000000..3b016c98b9f8a7741c929ffe97ab9f73c4516805 GIT binary patch literal 8051 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 D2=QWf literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/environment.pickle b/doc/_build/doctrees/environment.pickle new file mode 100644 index 0000000000000000000000000000000000000000..028b509fabef5259788a93d85341f0dd8d5b5cf4 GIT binary patch literal 28297 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 F|NmIur9l7y literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/index.doctree b/doc/_build/doctrees/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..9751228ed196c22b11bc20b349a2911e31626a10 GIT binary patch literal 5102 zcmcIo&yOQV6`oy>$M$&a`LUURHao~dkj#d793l>ABu?Ri7DfmmB!WUuxx4JDneOVQ zt9r&GB@!Gq+KGx&nhQb#gpdFUIKzLyAAk@N5(iHCFZf<{x7~KuyPHE+nw9I-t5>hy zkFUP!pEiH-zk3_*pX~>+pBWkI6BP$s=eA9Ph%1wSlt27g{(gRJJ1M)?ahCc#w;Ry# zg$#p~tNh*kSZvsq=Dv}!nlBJdu_ao?WYb6!^4gR=kx>$-M*B>i4pQ!Csg_qfycrbo z!FO+rh}9rr{w15jBeH~DHownjq@fIInH_A=4T{{B9~16~Ed+mQ;hxwhWunC>MQkUv$MQq9GpG zw?-n4_~@DiJY}P6J|0DJ$`fY9Xc3VS92-d$@YzYK;p@?(yV)40@4$IBO<5x9p?vTB ze6jTWe2}E^l|&W?Y;b-~0fuanDVLpr5zGuiZuH)o8xNy6 z$U-s+8I7yFoVdie{D;!uNMYTx{<3F95)W;;6}gs|c|nBUDk5|yjwtlv3y8NEAn9Mk z-brR+j>Vf$eFM=rv^({a)(pK-HzdAVl&@&ob`_1vkIfYi-mWiv5f9$^4CA^8 z%+h)I+Q7Un9*Bo1mCdT!T*$;xpq7`SFYT^QgjBN=76fJ2w@mDtl-C=z(0%Hr z;=XOVB1J!-rBdm-;P}ioSR(Te@-w>~aP)MXNHulimM@s1ZvG(Wc1J2d%mO}{>wjHrqz!M|#M|aU_EOD@czMO{fIKQ=9LIweF=hpU06LUdZWzm?YJjfqy*lh4r|a> ziie4Vws{M|a9QHd1+`*j)u0+H%ckhSokmf;t_BD$wyVOgV0gC@*n@AWK>B4n8#bl{ zIDkET>eQkXfBs?}LV)*S&7ECWirsM*0(rbp0@JE%rG86vXvPDKM73$Vdz$je>G_l^ zS8V5nr(dZKBYx*R|FPXkI8LRe-VFLR1Es=--IB^7du{7t?V)W~)@v~G*Y#&TwwrDf z+t4OJ_2YjedXn>ciToKs_|s}xRSI?s`atOgEFS+YnH0~iCbq_I^_np@%#W(*?%Z2dmk?Rew_=XSYZY!{`Oglzph#Q6$J(v z{Y{mEDn+;?o}QwAxfK1e(ig?}FDqlDp|Vg*!+)QV2JyG&X5c^TPPHxrAHR48#6M~t z*W-SGaQ=JU#`U<9;y<6ISjC+bKNn*QgL~rsMe7-x*V90{jpyiAnSgGU zi3Q|W2u^rxw+fug?VZ;hWBFyRcN{7%T^0vu8N%WrsMWB<;>C?n%{t;WpIw9cfZ25} zmE=}Dv@e;IDIGGpR6S6|Y07m$Zkr}H;BW5cEOp#Pno+3k*giNqQD<*Xly@pgL6~h@ zb7O$Oct7kDwRR7RNg68yWJX+mz`_hSO@+U<8)qiTj7RJ@KNI~k+X0M$M4C7Sd*}oV zyxXuS!JwPQF*JdGa+ZrN+bz7nWuh*LX_Uh(4KeEB?)JubI?ybV3OyMhoT20f0m`M5E5%iu4pG@X^)9<9u*n7Eiy zd-e;vpIrT5d17U;-J>(0{Io!bLYyhLy%3}@!z&LF4=}z9ZfEa-IK^q;fe4~_unn=x z+B?-17X;s|=5M}TrxkXSXe9!N#~j!Oo(^NX<;QA*5Fwf}sFhNmWhV9(B70Y&T6;kj zCEmQEkrXG8DCShLkg8(WNnAfsRk+npc;XRZ0Z;(b zAOR~&&}}$BwWj^NFecHbqY=dVq~NnBaXKC4_K-B4FVOTr0#P0?YTJ+xzx?LckduBo z@mI`~WMTf*$~^pG)H0VmJ(1i@+#d9*7A6X@SqE5(E)M9C(Y8nEiO8FS!~6iA#ygjT zb?xaJW!TIUawtMAQOd%!;0-uMn3xss|6606=g9LxO|r-P%JRT9+ONWeh#M-Uz|n=KJM0 z7?f=|DWLJzHn_T?uNk&ofD%vuMM<{~TFiI_gLGdq_E8%sGK1d2-GG-F-3#_$Nm_j0 zfEd;$^8vA0`ZNN0i-g7RWAQ{hzW4^<;^LHUSKFaEBDe0s5l?R2 None: scan(moduleinzipped) assert fixtures.calls == 1 + + +def test_scan_loader_file_close() -> None: + # __init__.py overwrites the 'scan' attribute with the function, so + # `import importscan.scan as x` resolves via getattr and yields the + # function; sys.modules gives the actual module object. + scan_mod = sys.modules["importscan.scan"] + + from .fixtures import package + + mock_file = MagicMock() + mock_loader = MagicMock() + mock_loader.file = mock_file + mock_spec = MagicMock() + mock_spec.loader = mock_loader + mock_importer = MagicMock() + mock_importer.find_spec.return_value = mock_spec + + # patch.object avoids ambiguity from importscan.__init__ re-exporting scan + with patch.object(scan_mod, "walk_packages") as mock_wp: + mock_wp.return_value = iter( + [(mock_importer, "importscan.tests.fixtures.package.module", False)] + ) + with patch.object(scan_mod, "import_module"): + scan(package) + + mock_file.close.assert_called_once() + + +class _LoaderUnderscoreGetFilename: + def _get_filename(self, modname: str | None = None) -> str: + return "some_module.py" + + +class _LoaderGetFilenameTypeError: + def get_filename(self, modname: str | None = None) -> str: + if modname is not None: + raise TypeError + return "some_module.py" + + +class _LoaderPycFilename: + def get_filename(self, modname: str | None = None) -> str: + return "some_module.pyc" + + +def test_import_module_underscore_get_filename() -> None: + loader = _LoaderUnderscoreGetFilename() + with patch("builtins.__import__"): + import_module("somemodule", loader, None) # type: ignore[arg-type] + + +def test_import_module_get_filename_typeerror() -> None: + loader = _LoaderGetFilenameTypeError() + with patch("builtins.__import__"): + import_module("somemodule", loader, None) # type: ignore[arg-type] + + +def test_import_module_pyc_skipped() -> None: + loader = _LoaderPycFilename() + with patch("builtins.__import__") as mock_import: + import_module("somemodule", loader, None) # type: ignore[arg-type] + mock_import.assert_not_called() + + +def test_walk_packages_importerror_subpackage() -> None: + from .fixtures import importerror_pkg + + with raises(ImportError): + scan(importerror_pkg) + + +def test_walk_packages_importerror_subpackage_handle_error() -> None: + from .fixtures import importerror_pkg_handle_error + + errors: list[str] = [] + + def handle_error(name: str, e: Exception) -> None: + errors.append(name) + + scan(importerror_pkg_handle_error, handle_error=handle_error) + assert len(errors) == 1 From ecd21c70064850f92bb2bbd022ce48a3aac424b6 Mon Sep 17 00:00:00 2001 From: Henri Hulski Date: Mon, 10 Aug 2026 18:52:43 +0200 Subject: [PATCH 2/7] docs: update documentation and adjust sphinx config --- README.rst | 2 +- doc/conf.py | 47 +++++++++++++++++++++++++++++++++++----------- doc/developing.rst | 27 +++++++++++--------------- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/README.rst b/README.rst index f14e91b..955e4a6 100644 --- a/README.rst +++ b/README.rst @@ -20,4 +20,4 @@ import a package and its sub-modules and sub-packages. Documentation_. -.. _Documentation: http://importscan.readthedocs.org +.. _Documentation: https://importscan.readthedocs.org diff --git a/doc/conf.py b/doc/conf.py index 8f723d2..701b690 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -10,7 +10,7 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import pkg_resources +from importlib import metadata # 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 @@ -24,16 +24,20 @@ # 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", "sphinx.ext.intersphinx"] +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx.ext.doctest", +] autoclass_content = "both" autodoc_member_order = "groupwise" intersphinx_mapping = { - "reg": ("http://reg.readthedocs.io/en/latest", None), - "webob": ("http://docs.webob.org/en/latest", None), - "bowerstatic": ("http://bowerstatic.readthedocs.io/en/latest", None), + "reg": ("https://reg.readthedocs.io/en/latest", None), + "webob": ("https://docs.pylonsproject.org/projects/webob/en/latest", None), + "bowerstatic": ("https://bowerstatic.readthedocs.io/en/latest", None), } # Add any paths that contain templates here, relative to this directory. @@ -60,7 +64,28 @@ # built documents. # # The short X.Y version. -version = pkg_resources.get_distribution("importscan").version +try: + version = metadata.version("importscan") +except metadata.PackageNotFoundError: + # Fallback for ReadTheDocs and other environments where the package isn't installed + # Try to get version from pyproject.toml + import os + import re + + try: + pyproject_path = os.path.join( + os.path.dirname(__file__), "..", "pyproject.toml" + ) + with open(pyproject_path) as f: + content = f.read() + # Simple regex to extract version + version_match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', content) + if version_match: + version = version_match.group(1) + else: + version = "0.0.0" + except (FileNotFoundError, Exception): + version = "0.0.0" # The full version, including alpha/beta/rc tags. release = version @@ -69,7 +94,7 @@ # # 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 +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -82,7 +107,7 @@ exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all -# documents. +# documents.n # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. @@ -142,7 +167,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = [] +html_static_path: list[str] = [] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -209,7 +234,7 @@ # -- Options for LaTeX output --------------------------------------------- -latex_elements = { +latex_elements: dict[str, str] = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). @@ -297,4 +322,4 @@ # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {"https://docs.python.org/": None} +# intersphinx_mapping = {"https://docs.python.org/": None} diff --git a/doc/developing.rst b/doc/developing.rst index b235452..cea7200 100644 --- a/doc/developing.rst +++ b/doc/developing.rst @@ -8,7 +8,7 @@ Install Importscan for development Clone Importscan from github:: - $ git clone git@github.com:faassen/importscan.git + $ git clone git@github.com:morepath/importscan.git If this doesn't work and you get an error 'Permission denied (publickey)', you need to upload your ssh public key to github_. @@ -21,15 +21,11 @@ Make sure you have virtualenv_ installed. Create a new virtualenv for Python 3 inside the importscan directory:: - $ virtualenv -p python3 env/py3 + $ python -m venv --upgrade-deps .venv Activate the virtualenv:: - $ source env/py3/bin/activate - -Make sure you have recent setuptools and pip installed:: - - $ pip install -U setuptools pip + $ source .venv/bin/activate Install the various dependencies and development tools from develop_requirements.txt:: @@ -42,9 +38,8 @@ For upgrading the requirements just run the command again. The following commands work only if you have the virtualenv activated. -.. _github: https://help.github.com/articles/generating-an-ssh-key - -.. _virtualenv: https://pypi.python.org/pypi/virtualenv +.. _github: https://docs.github.com/en/authentication/connecting-to-github-with-ssh +.. _virtualenv: https://pypi.org/project/virtualenv Install pre-commit hook for Black integration --------------------------------------------- @@ -54,7 +49,7 @@ install the `pre-commit hook`_ for Black integration before committing:: $ pre-commit install -.. _`pre-commit hook`: https://black.readthedocs.io/en/stable/version_control_integration.html +.. _`pre-commit hook`: https://black.readthedocs.io/en/stable/integrations/source_version_control.html Running the tests ----------------- @@ -71,7 +66,7 @@ You can then point your web browser to the ``htmlcov/index.html`` file in the project directory and click on modules to see detailed coverage information. -.. _`py.test`: http://pytest.org/latest/ +.. _`py.test`: https://pytest.org/latest/ Black ----- @@ -115,11 +110,11 @@ To also show cyclomatic complexity, use this command:: $ flake8 --max-complexity=10 importscan -.. _flake8: https://pypi.python.org/pypi/flake8 +.. _flake8: https://pypi.org/project/flake8 -.. _pyflakes: https://pypi.python.org/pypi/pyflakes +.. _pyflakes: https://pypi.org/project/pyflakes -.. _pep8: http://www.python.org/dev/peps/pep-0008/ +.. _pep8: https://peps.python.org/pep-0008 .. _`cyclomatic complexity`: https://en.wikipedia.org/wiki/Cyclomatic_complexity @@ -151,4 +146,4 @@ You can also specify a test environment to run e.g.:: $ tox -e lint $ tox -e coverage -.. _pyenv: https://github.com/yyuu/pyenv +.. _pyenv: https://github.com/pyenv/pyenv From 958cd0c10774a3c3e74f20282519d2e6e16e7157 Mon Sep 17 00:00:00 2001 From: Henri Hulski Date: Mon, 10 Aug 2026 18:56:26 +0200 Subject: [PATCH 3/7] typing: fix types for pyright strict mode and adjust typing configuration --- importscan/scan.py | 22 ++++++++++++++-------- pyproject.toml | 7 ++++++- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/importscan/scan.py b/importscan/scan.py index f081af5..8c425d6 100644 --- a/importscan/scan.py +++ b/importscan/scan.py @@ -2,13 +2,14 @@ import sys from pkgutil import iter_modules -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, cast if TYPE_CHECKING: from collections.abc import Callable, Generator, Iterable from importlib.abc import Loader from types import ModuleType from typing_extensions import TypeIs + from importscan.types import IgnoreModule, ModuleInfo, StrOrBytesPath @@ -107,7 +108,7 @@ def handle_error(name, e): if not hasattr(package, "__path__"): return - for importer, modname, ispkg in walk_packages( + for importer, modname, _ispkg in walk_packages( package.__path__, package.__name__ + ".", is_ignored=is_ignored, @@ -125,17 +126,18 @@ def handle_error(name, e): # Also why do we do only use `import_module` here, but not # in `walk_packages`? Doesn't that mean that the additional # check in `import_module` doesn't do anything for packages? - loader = importer.find_spec(modname).loader # type: ignore + spec = importer.find_spec(modname, None) + loader = spec.loader if spec else None assert loader is not None try: import_module(modname, loader, handle_error) finally: if hasattr(loader, "file") and hasattr( - loader.file, # pyright: ignore[reportAttributeAccessIssue] + getattr(loader, "file"), "close", ): - loader.file.close() # pyright: ignore[reportAttributeAccessIssue] + getattr(loader, "file").close() def import_module( @@ -143,13 +145,17 @@ def import_module( loader: Loader, handle_error: Callable[[str, Exception], object] | None, ) -> None: - get_filename = getattr(loader, "get_filename", None) + get_filename: Callable[..., str] | None = getattr( + loader, "get_filename", None + ) if get_filename is None: get_filename = loader._get_filename # type: ignore[attr-defined] try: - fn = get_filename(modname) + fn: str = cast( + str, get_filename(modname) # pyright: ignore[reportOptionalCall] + ) except TypeError: - fn = get_filename() + fn = cast(str, get_filename()) # pyright: ignore[reportOptionalCall] # only scan non-orphaned source files and package directories if fn.endswith((".pyc", ".pyo", "$py.class")): return diff --git a/pyproject.toml b/pyproject.toml index acb45e6..c2e02e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,15 +71,20 @@ max-line-length = 88 python_version = "3.10" strict = true warn_unreachable = true +warn_unused_ignores = true [[tool.mypy.overrides]] module = "importscan.tests.fixtures.*" ignore_errors = true [tool.pyright] +typeCheckingMode = "strict" +venvPath = "." +venv = ".venv" exclude = [ - "**/tests/fixtures/**", + "**/tests/fixtures/**", "**/__pycache__", ] +reportUnnecessaryTypeIgnoreComment = "warning" [tool.tox] requires = ["tox>=4"] From d3f23de9f493716bc5bd8b20f611c5fae8385fc2 Mon Sep 17 00:00:00 2001 From: Henri Hulski Date: Mon, 10 Aug 2026 19:03:33 +0200 Subject: [PATCH 4/7] chore: fix setup --- .gitignore | 6 +++++- develop_requirements.txt | 5 +---- pyproject.toml | 28 +++++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index b250aa7..456e39d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,7 @@ pip-wheel-metadata # Virtualenv /env/ -/src/ +/.venv/ # Unit test / coverage reports .cache @@ -20,4 +20,8 @@ htmlcov .tox # Sphinx documentation +/doc/_build/ /doc/build/ + +# Type checkers +.mypy_cache/ diff --git a/develop_requirements.txt b/develop_requirements.txt index e076727..68e0793 100644 --- a/develop_requirements.txt +++ b/develop_requirements.txt @@ -1,11 +1,8 @@ # development --e '.[test,coverage,lint]' +-e '.[test,coverage,lint,docs,mypy,pyright]' pre-commit tox >= 4 radon -# documentation -sphinx - # releaser zest.releaser[recommended] diff --git a/pyproject.toml b/pyproject.toml index c2e02e6..da3a6a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ Changelog = "https://github.com/morepath/importscan/blob/master/CHANGES.txt" [project.optional-dependencies] test = ["pytest >= 8", "pytest-env"] coverage = ["pytest-cov"] -lint = ["black", "flake8", "flake8-pyproject"] +lint = ["black", "flake8", "flake8-pyproject", "isort"] docs = ["sphinx"] mypy = ["mypy", "pytest"] pyright = ["pyright", "pytest"] @@ -159,3 +159,29 @@ commands = [ ["pyright", "importscan", "--pythonversion", "3.13"], ["pyright", "importscan", "--pythonversion", "3.14"], ] + +[tool.black] +target-version = ['py310', 'py311', 'py312', 'py313', 'py314'] +include = '\.pyi?$' +exclude = ''' +( + /( + \.git + | \.tox + | env + | venv + | .venv + | __pycache__ + | build + | dist + | src + )/ +) +''' + +[tool.isort] +profile = 'black' +py_version = 310 +skip_gitignore = true +extra_standard_library = ["typing_extensions"] +known_first_party = ["dectate", "importscan", "reg"] From 7c15c4c123836f839cf5c7d0f32f84ca142e665f Mon Sep 17 00:00:00 2001 From: Henri Hulski Date: Mon, 10 Aug 2026 19:04:30 +0200 Subject: [PATCH 5/7] deploy: fix comment --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5469b3a..75bcba0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,7 +34,7 @@ jobs: run: "python -m tox" - name: "Report to coveralls" - # coverage is only created in the py39 environment + # coverage is only created in the py314 environment # --service=github is a workaround for bug # https://github.com/coveralls-clients/coveralls-python/issues/251 if: "matrix.python-version == '3.14'" From d5d010b34c037890735060aca3c6d919ec42dae0 Mon Sep 17 00:00:00 2001 From: Henri Hulski Date: Mon, 10 Aug 2026 19:05:48 +0200 Subject: [PATCH 6/7] docs: update CHANGES --- CHANGES.txt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 80ea5d5..4b8b2dc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,8 +1,10 @@ CHANGES ******* -0.3 (unreleased) ----------------- +1.0.0 (unreleased) +------------------ + +- Add type annotations. - Add support for Python 3.10 - 3.14. @@ -12,6 +14,10 @@ CHANGES - Use GitHub Actions for CI. +- Add tests for 100% coverage. + +- Update docs. + 0.2 (2020-01-29) ---------------- From d80e4591d1615e17ba2a90505fdcdfa5a4776990 Mon Sep 17 00:00:00 2001 From: Henri Hulski Date: Mon, 10 Aug 2026 22:28:55 +0200 Subject: [PATCH 7/7] fix: skip importing packages in scan() since they're already imported in walk_packages() --- CHANGES.txt | 2 ++ importscan/scan.py | 16 +++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4b8b2dc..661ce8c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,8 @@ CHANGES 1.0.0 (unreleased) ------------------ +- Skip importing packages in scan() since they're already imported in walk_packages(). + - Add type annotations. - Add support for Python 3.10 - 3.14. diff --git a/importscan/scan.py b/importscan/scan.py index 8c425d6..424607c 100644 --- a/importscan/scan.py +++ b/importscan/scan.py @@ -108,7 +108,7 @@ def handle_error(name, e): if not hasattr(package, "__path__"): return - for importer, modname, _ispkg in walk_packages( + for importer, modname, ispkg in walk_packages( package.__path__, package.__name__ + ".", is_ignored=is_ignored, @@ -120,12 +120,14 @@ def handle_error(name, e): # into iter_modules for submodules/subpackages. There's also # the additional issue that not all finders will implement the # non-standard iter_modules method, but without it there's - # no way to list all of the modules. Also since walk_packages - # already imports all of the packages, why are we importing - # them again here? Shouldn't we only import modules here? - # Also why do we do only use `import_module` here, but not - # in `walk_packages`? Doesn't that mean that the additional - # check in `import_module` doesn't do anything for packages? + # no way to list all of the modules. + # + # Note: Packages are already imported in walk_packages() to access + # their __path__ for recursion and have already been error-checked. + # Only modules need to be imported here. + if ispkg: + continue + spec = importer.find_spec(modname, None) loader = spec.loader if spec else None assert loader is not None