From cda7cfc035d30ddf89963bf9012cd9c255deeb60 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Mon, 21 Oct 2024 00:57:10 -0400 Subject: [PATCH] fix: use alternate scheme for importing multipart Signed-off-by: Henry Schreiner --- .github/workflows/main.yml | 3 +-- _python_multipart.pth | 1 - _python_multipart_loader.py | 37 ------------------------------------- multipart/__init__.py | 20 ++++++++++++++++++++ multipart/decoders.py | 1 + multipart/exceptions.py | 1 + multipart/multipart.py | 1 + noxfile.py | 28 +++++++++++++++++++++------- pyproject.toml | 8 +++++--- scripts/README.md | 1 + scripts/check | 2 +- scripts/rename | 7 +++++++ 12 files changed, 59 insertions(+), 51 deletions(-) delete mode 100644 _python_multipart.pth delete mode 100644 _python_multipart_loader.py create mode 100644 multipart/__init__.py create mode 100644 multipart/decoders.py create mode 100644 multipart/exceptions.py create mode 100644 multipart/multipart.py create mode 100755 scripts/rename diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fdeed33..4ceafb7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,8 +34,7 @@ jobs: run: scripts/test - name: Run rename test - run: uvx nox -s rename -P ${{ matrix.python-version }} - + run: scripts/rename # https://github.com/marketplace/actions/alls-green#why used for branch protection checks check: if: always() diff --git a/_python_multipart.pth b/_python_multipart.pth deleted file mode 100644 index e681c13..0000000 --- a/_python_multipart.pth +++ /dev/null @@ -1 +0,0 @@ -import _python_multipart_loader diff --git a/_python_multipart_loader.py b/_python_multipart_loader.py deleted file mode 100644 index 7d34377..0000000 --- a/_python_multipart_loader.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import annotations - -# The purpose of this file is to allow `import multipart` to continue to work -# unless `multipart` (the PyPI package) is also installed, in which case -# a collision is avoided, and `import multipart` is no longer injected. -import importlib -import importlib.abc -import importlib.machinery -import importlib.util -import sys -import warnings - - -class PythonMultipartCompatFinder(importlib.abc.MetaPathFinder): - def find_spec( - self, fullname: str, path: object = None, target: object = None - ) -> importlib.machinery.ModuleSpec | None: - if fullname != "multipart": - return None - old_sys_meta_path = sys.meta_path - try: - sys.meta_path = [p for p in sys.meta_path if not isinstance(p, type(self))] - if multipart := importlib.util.find_spec("multipart"): - return multipart - - warnings.warn("Please use `import python_multipart` instead.", FutureWarning, stacklevel=2) - sys.modules["multipart"] = importlib.import_module("python_multipart") - return importlib.util.find_spec("python_multipart") - finally: - sys.meta_path = old_sys_meta_path - - -def install() -> None: - sys.meta_path.insert(0, PythonMultipartCompatFinder()) - - -install() diff --git a/multipart/__init__.py b/multipart/__init__.py new file mode 100644 index 0000000..212af4e --- /dev/null +++ b/multipart/__init__.py @@ -0,0 +1,20 @@ +# This only works if using a file system, other loaders not implemented. + +import importlib.util +import sys +import warnings +from pathlib import Path + +for p in sys.path: + file_path = Path(p, "multipart.py") + if file_path.is_file(): + spec = importlib.util.spec_from_file_location("multipart", file_path) + assert spec is not None, f"{file_path} found but not loadable!" + module = importlib.util.module_from_spec(spec) + sys.modules["multipart"] = module + assert spec.loader is not None, f"{file_path} must be loadable!" + spec.loader.exec_module(module) + break +else: + warnings.warn("Please use `import python_multipart` instead.", FutureWarning, stacklevel=2) + from python_multipart import * diff --git a/multipart/decoders.py b/multipart/decoders.py new file mode 100644 index 0000000..31acdfb --- /dev/null +++ b/multipart/decoders.py @@ -0,0 +1 @@ +from python_multipart.decoders import * diff --git a/multipart/exceptions.py b/multipart/exceptions.py new file mode 100644 index 0000000..36815d1 --- /dev/null +++ b/multipart/exceptions.py @@ -0,0 +1 @@ +from python_multipart.exceptions import * diff --git a/multipart/multipart.py b/multipart/multipart.py new file mode 100644 index 0000000..7bf567d --- /dev/null +++ b/multipart/multipart.py @@ -0,0 +1 @@ +from python_multipart.multipart import * diff --git a/noxfile.py b/noxfile.py index fda8050..1df0fd7 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,16 +1,12 @@ +import inspect + import nox nox.needs_version = ">=2024.4.15" nox.options.default_venv_backend = "uv|virtualenv" -ALL_PYTHONS = [ - c.split()[-1] - for c in nox.project.load_toml("pyproject.toml")["project"]["classifiers"] - if c.startswith("Programming Language :: Python :: 3.") -] - -@nox.session(python=ALL_PYTHONS) +@nox.session def rename(session: nox.Session) -> None: session.install(".") assert "import python_multipart" in session.run("python", "-c", "import multipart", silent=True) @@ -27,3 +23,21 @@ def rename(session: nox.Session) -> None: assert "import python_multipart" not in session.run( "python", "-c", "import python_multipart; python_multipart.parse_form", silent=True ) + + +@nox.session +def rename_inline(session: nox.Session) -> None: + session.install("pip") + res = session.run( + "python", + "-c", + inspect.cleandoc(""" + import subprocess + + subprocess.run(["pip", "install", "."]) + + import multipart + """), + silent=True, + ) + assert "FutureWarning: Please use `import python_multipart` instead." in res diff --git a/pyproject.toml b/pyproject.toml index 1a81077..29206de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,9 +70,8 @@ path = "python_multipart/__init__.py" [tool.hatch.build.targets.sdist] include = ["/python_multipart", "/tests", "CHANGELOG.md", "LICENSE.txt", "_python_multipart.pth", "_python_multipart_loader.py"] -[tool.hatch.build.targets.wheel.force-include] -"_python_multipart.pth" = "_python_multipart.pth" -"_python_multipart_loader.py" = "_python_multipart_loader.py" +[tool.hatch.build.targets.wheel] +packages = ["python_multipart", "multipart"] [tool.mypy] strict = true @@ -91,6 +90,9 @@ skip-magic-trailing-comma = true combine-as-imports = true split-on-trailing-comma = false +[tool.ruff.lint.per-file-ignores] +"multipart/*.py" = ["F403"] + [tool.coverage.run] branch = false omit = ["tests/*"] diff --git a/scripts/README.md b/scripts/README.md index 1742ebd..0d56d9a 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -4,5 +4,6 @@ * `scripts/test` - Run the test suite. * `scripts/lint` - Run the code format. * `scripts/check` - Run the lint in check mode, and the type checker. +* `scripts/rename` - Check that the backward-compat `multipart` name works as expected Styled after GitHub's ["Scripts to Rule Them All"](https://github.com/github/scripts-to-rule-them-all). diff --git a/scripts/check b/scripts/check index f38e9c0..141f2a9 100755 --- a/scripts/check +++ b/scripts/check @@ -2,7 +2,7 @@ set -x -SOURCE_FILES="python_multipart tests" +SOURCE_FILES="python_multipart multipart tests" uvx ruff format --check --diff $SOURCE_FILES uvx ruff check $SOURCE_FILES diff --git a/scripts/rename b/scripts/rename new file mode 100755 index 0000000..251cc1d --- /dev/null +++ b/scripts/rename @@ -0,0 +1,7 @@ +#!/bin/sh -e + +set -x + +uvx nox -s rename + +uvx nox -s rename_inline