diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 2ab96b3f..8eb5a7a8 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -20,11 +20,8 @@ jobs: python-version: ["3.11", "3.12", "3.13", "3.14"] fail-fast: false runs-on: ${{ matrix.os }} - defaults: - run: - shell: bash -l {0} - env: - PIP_BREAK_SYSTEM_PACKAGES: 1 + # removed: defaults.run.shell: bash -l {0} + # removed: env.PIP_BREAK_SYSTEM_PACKAGES steps: - name: Checkout uses: actions/checkout@v2 @@ -40,15 +37,16 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - name: Update build tools - run: python3 -m pip install --upgrade pip - if: matrix.os != 'macos-latest' + allow-prereleases: true + - name: Check interpreter + run: | + which -a python python3 + python -c "import sys; print(sys.executable, sys.prefix)" - name: Install Package - run: python3 -m pip install -e .[test] + run: python -m pip install -e ".[test]" - name: Install Extras Package run: python3 -m pip install -e ./extras[test,application,image,vnd_openxmlformats] - name: MyPy - if: ${{ matrix.python-version != '3.11' }} run: mypy --install-types --non-interactive --no-warn-unused-ignores --python-version "${{ matrix.python-version }}" . - name: Pytest run: pytest -vvs --cov fileformats --cov-config .coveragerc --cov-report xml . diff --git a/extras/pyproject.toml b/extras/pyproject.toml index 434ccd63..8e3ce066 100644 --- a/extras/pyproject.toml +++ b/extras/pyproject.toml @@ -7,7 +7,7 @@ name = "fileformats-extras" description = "Extra methods for accessing and manipulating the underlying data referenced by fileformats classes" readme = "README.rst" requires-python = ">=3.11" -dependencies = ["fileformats", "pydra >=1.0a"] +dependencies = ["fileformats >=0.17.5", "pydra >=1.0a"] license = { file = "LICENSE" } authors = [{ name = "Thomas G. Close", email = "tom.g.close@gmail.com" }] maintainers = [{ name = "Thomas G. Close", email = "tom.g.close@gmail.com" }] diff --git a/fileformats/core/extras.py b/fileformats/core/extras.py index 993522a0..3c84f179 100644 --- a/fileformats/core/extras.py +++ b/fileformats/core/extras.py @@ -64,6 +64,14 @@ def decorated_extra(obj: DataType, *args: ty.Any, **kwargs: ty.Any) -> ty.Any: '. Was not able to check whether an "extras" package ' f"({xtra.pypi}) exists on PyPI or not" ) + elif xtra.pkg: + msg += ( + f'. The "{xtra.pkg}" extras module is installed but doesn\'t ' + "register an implementation, which can happen when it is out of " + 'date with respect to the installed "fileformats" package, so ' + "try upgrading it (e.g. " + f"'pip install --upgrade {xtra.pypi}') and check again" + ) raise FileFormatsExtrasNotImplementedError(msg) from None # Store single dispatch method on the decorated function so we can register diff --git a/fileformats/core/identification.py b/fileformats/core/identification.py index 869bf9ba..6a06d119 100644 --- a/fileformats/core/identification.py +++ b/fileformats/core/identification.py @@ -38,6 +38,11 @@ "testing", "vendor.testing", ] +# Namespaces that are shipped within the main "fileformats" package, and therefore have +# their "extras" implementations bundled together in the "fileformats-extras" package. +# Note that "generic" is included here even though it isn't a "standard" type registry, +# since generic types are defined in the main package alongside the standard ones +BUNDLED_EXTRAS_NAMESPACES = ALL_STANDARD_TYPE_REGISTRIES + ["generic"] def find_matching( diff --git a/fileformats/core/utils.py b/fileformats/core/utils.py index 21688ea7..41b5ce78 100644 --- a/fileformats/core/utils.py +++ b/fileformats/core/utils.py @@ -272,7 +272,7 @@ def import_extras_module(klass: ty.Type["fileformats.core.DataType"]) -> ExtrasM sub_pkg : str the name of the sub-package that was attempted to be loaded """ - from .identification import IANA_MIME_TYPE_REGISTRIES + from .identification import BUNDLED_EXTRAS_NAMESPACES # Check for Mock class try: @@ -293,7 +293,7 @@ def import_extras_module(klass: ty.Type["fileformats.core.DataType"]) -> ExtrasM ) return ExtrasModule(True, None, None) extras_pkg = "fileformats.extras." + sub_pkg.replace("-", "_") - if sub_pkg in IANA_MIME_TYPE_REGISTRIES + ["testing"]: + if sub_pkg in BUNDLED_EXTRAS_NAMESPACES: extras_pypi = "fileformats-extras" elif klass.vendor: extras_pypi = f"fileformats-{klass.vendor}-extras" @@ -302,7 +302,13 @@ def import_extras_module(klass: ty.Type["fileformats.core.DataType"]) -> ExtrasM try: importlib.import_module(extras_pkg) except ModuleNotFoundError as e: - if str(e) != f"No module named '{extras_pkg}'": + # The missing module can be a parent of the extras package rather than the + # package itself, e.g. importing "fileformats.extras.vendor.." + # when there is no "fileformats.extras.vendor." sub-package at all. + # Errors raised from *within* the extras module are still propagated + if not e.name or not ( + extras_pkg == e.name or extras_pkg.startswith(e.name + ".") + ): raise extras_imported = False else: