Skip to content

Upgrade maturin #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: v5.0.0
hooks:
- id: check-yaml
- id: check-toml
- id: end-of-file-fixer
- id: trailing-whitespace
- id: mixed-line-ending
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.9
rev: v0.7.4
hooks:
- id: ruff-format
- id: ruff
args: [ --fix ]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.0
rev: v1.13.0
hooks:
# note: mypy runs in an isolated environment and so has no access to third party packages
- id: mypy
Expand All @@ -26,6 +26,6 @@ repos:
hooks:
- id: codespell
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.41.0
rev: v0.42.0
hooks:
- id: markdownlint-fix
10 changes: 6 additions & 4 deletions src/maturin_import_hook/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,12 @@ def _main() -> None:
install.add_argument(
"--user",
action="store_true",
help="whether to install into usercustomize.py instead of sitecustomize.py. "
"Note that usercustomize.py is shared between virtualenvs of the same interpreter version and is not loaded "
"unless the virtualenv is created with the `--system-site-packages` argument. Use `site info` to check "
"whether usercustomize.py is loaded the current interpreter.",
help=(
"whether to install into usercustomize.py instead of sitecustomize.py. "
"Note that usercustomize.py is shared between virtualenvs of the same interpreter version and is "
"not loaded unless the virtualenv is created with the `--system-site-packages` argument. "
"Use `site info` to check whether usercustomize.py is loaded the current interpreter."
),
)

uninstall = site_sub_actions.add_parser(
Expand Down
2 changes: 1 addition & 1 deletion src/maturin_import_hook/_building.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def get_maturin_version(maturin_path: Path) -> tuple[int, int, int]:
if not success:
msg = f'running "{maturin_path} --version" failed'
raise MaturinError(msg)
match = re.fullmatch(r"maturin ([0-9]+)\.([0-9]+)\.([0-9]+)\n", output)
match = re.fullmatch(r"maturin ([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?\n", output)
if match is None:
msg = f'unexpected version string: "{output}"'
raise MaturinError(msg)
Expand Down
28 changes: 19 additions & 9 deletions src/maturin_import_hook/_resolve_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def get_value(self, keys: list[str], required_type: type[_T]) -> Optional[_T]:

def find_cargo_manifest(project_dir: Path) -> Optional[Path]:
pyproject_path = project_dir / "pyproject.toml"
if pyproject_path.exists():
if pyproject_path.is_file():
pyproject_data = pyproject_path.read_text()
if "manifest-path" in pyproject_data:
pyproject = _TomlFile.from_string(pyproject_path, pyproject_data)
Expand All @@ -69,17 +69,17 @@ def find_cargo_manifest(project_dir: Path) -> Optional[Path]:
return project_dir / relative_manifest_path

manifest_path = project_dir / "Cargo.toml"
if manifest_path.exists():
if manifest_path.is_file():
return manifest_path
manifest_path = project_dir / "rust/Cargo.toml"
if manifest_path.exists():
if manifest_path.is_file():
return manifest_path
return None


def is_maybe_maturin_project(project_dir: Path) -> bool:
def is_maybe_maturin_project(directory: Path) -> bool:
"""note: this function does not check if this really is a maturin project for simplicity."""
return (project_dir / "pyproject.toml").exists() and find_cargo_manifest(project_dir) is not None
return (directory / "pyproject.toml").is_file() and find_cargo_manifest(directory) is not None


class ProjectResolver:
Expand Down Expand Up @@ -142,7 +142,7 @@ def all_path_dependencies(self) -> list[Path]:
def _find_all_path_dependencies(immediate_path_dependencies: list[Path]) -> list[Path]:
if not immediate_path_dependencies:
return []
all_path_dependencies = set()
all_path_dependencies: set[Path] = set()
to_search = immediate_path_dependencies.copy()
while to_search:
dependency_project_dir = to_search.pop()
Expand Down Expand Up @@ -170,6 +170,9 @@ def _resolve_project(project_dir: Path) -> MaturinProject:
msg = "no pyproject.toml found"
raise _ProjectResolveError(msg)
pyproject = _TomlFile.load(pyproject_path)
if not _is_valid_pyproject(pyproject):
msg = "pyproject.toml is invalid (does not have required fields)"
raise _ProjectResolveError(msg)

manifest_path = find_cargo_manifest(project_dir)
if manifest_path is None:
Expand Down Expand Up @@ -203,6 +206,13 @@ def _resolve_project(project_dir: Path) -> MaturinProject:
)


def _is_valid_pyproject(pyproject: _TomlFile) -> bool:
"""in maturin serde is used to load into a `PyProjectToml` struct.
This function should match whether the toml would parse correctly"""
# it should be sufficient to check the required fields rather than match the serde parsing logic exactly
return pyproject.get_value(["build-system", "requires"], list) is not None


def _resolve_rust_module(python_dir: Path, module_name: str) -> tuple[Path, Path, str]:
"""This follows the same logic as project_layout.rs (ProjectLayout::determine).

Expand Down Expand Up @@ -244,11 +254,11 @@ def _resolve_module_name(pyproject: _TomlFile, cargo: _TomlFile) -> Optional[str


def _get_immediate_path_dependencies(manifest_dir_path: Path, cargo: _TomlFile) -> list[Path]:
path_dependencies = []
path_dependencies: list[Path] = []
for dependency in cargo.get_value_or_default(["dependencies"], dict, {}).values():
if isinstance(dependency, dict):
relative_path = dependency.get("path", None)
if relative_path is not None:
relative_path: Any = dependency.get("path", None)
if relative_path is not None and isinstance(relative_path, str):
path_dependencies.append((manifest_dir_path / relative_path).resolve())
return path_dependencies

Expand Down
4 changes: 3 additions & 1 deletion src/maturin_import_hook/project_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def find_maturin(self) -> Path:
return self._maturin_path

def invalidate_caches(self) -> None:
"""called by importlib.invalidate_caches()"""
"""called by `importlib.invalidate_caches()`"""
logger.info("clearing cache")
self._resolver.clear_cache()
_find_maturin_project_above.cache_clear()
Expand Down Expand Up @@ -172,6 +172,8 @@ def find_spec(
logger.info('rebuilt and loaded package "%s" in %.3fs', package_name, duration)
else:
logger.debug('loaded package "%s" in %.3fs', package_name, duration)
elif logger.isEnabledFor(logging.DEBUG):
logger.debug('%s did not find "%s"', type(self).__name__, package_name)
return spec

def _handle_reload(self, package_name: str, spec: ModuleSpec) -> ModuleSpec:
Expand Down
2 changes: 2 additions & 0 deletions src/maturin_import_hook/rust_file_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ def find_spec(
logger.info('rebuilt and loaded module "%s" in %.3fs', fullname, duration)
else:
logger.debug('loaded module "%s" in %.3fs', fullname, duration)
elif logger.isEnabledFor(logging.DEBUG):
logger.debug('%s did not find "%s"', type(self).__name__, fullname)

return spec

Expand Down
7 changes: 4 additions & 3 deletions src/maturin_import_hook/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def to_args(self) -> list[str]:
class MaturinBuildSettings(MaturinSettings):
"""settings for `maturin build`."""

skip_auditwheel: bool = False
auditwheel: Optional[str] = None
zig: bool = False

@staticmethod
Expand All @@ -108,8 +108,9 @@ def supported_commands() -> set[str]:

def to_args(self) -> list[str]:
args = []
if self.skip_auditwheel:
args.append("--skip-auditwheel")
if self.auditwheel is not None:
args.append("--auditwheel")
args.append(self.auditwheel)
if self.zig:
args.append("--zig")
args.extend(super().to_args())
Expand Down
17 changes: 16 additions & 1 deletion tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,27 @@ To update maturin:

- update the submodule to the maturin commit you want to update to
- re-run the `package_resolver` to update `resolved.json` (see `package_resolver/README.md` for instructions)
- update `requirements.txt` to match the packages and versions used by the maturin ci (`.github/workflows.test.yml`)
- update `requirements.txt` to match the packages and versions installed by the maturin ci
(see `pip` and `uv` commands in `maturin/.github/workflows/test.yml`)
- check the `uniffi` package version listed in the `Cargo.toml` of any of the `uniffi-*`
test crates and update `uniffi-bindgen` in `requirements.txt` to match.
- check that no crates have been added to `test-crates` that should be excluded from the import hook tests.
If so, add them to `IGNORED_TEST_CRATES` in `common.py`
- upgrade the test packages in `test_import_hook/*_helpers`
- check what version of `pyo3` is used by the command: `maturin new --bindings pyo3`
- update the version check in the import hook to ensure it allows using the new version
- run the tests to ensure everything still works

Released versions of the import hook should use a tagged version of maturin, but during development, in order
to use a specific maturin commit:

- use `maturin @ git+https://github.com/PyO3/maturin@xxxxxxx` in `requirements.txt`
- where `xxxxxxx` is a git commit hash
- `export MATURIN_SETUP_ARGS="--features=scaffolding"` before running `runner.py`
- the `setup.py` of `maturin` uses this environment variable when building from source. the `scaffolding` feature
is required for `maturin new`.
- Make sure a cached built version of this commit is not available because `uv` doesn't know about the
environment variable. run `uv cache clean maturin` to remove any cached versions.

## Notes

Expand Down
2 changes: 1 addition & 1 deletion tests/maturin
Submodule maturin updated 158 files
Loading
Loading