Skip to content

Commit

Permalink
fix: make pumpkin-py build without any additional installs (#113)
Browse files Browse the repository at this point in the history
Running `cargo build` in the project root directory (not necessarily
useful, I did it by accident) means you also build `pumpkin-py`.
However, I have a new laptop so I didn't yet have need to install the
development headers for Python (`python3-dev` package on APT). This
causes the the build to fail with the below linker error:

```
  = note: /usr/bin/ld: cannot find -lpython3.13: No such file or directory
          collect2: error: ld returned 1 exit status
```

This confused me (I've used PyO3 before), it's just an extension module
(you're calling Rust from Python so you always have an interpreter) so
why would you need the dev headers, which you usually only need if
you're calling Python from a Rust executable
(https://pyo3.rs/v0.22.6/index.html?highlight=python3-dev#using-python-from-rust).

I then noticed the `extension-module` feature is not enabled in the
`Cargo.toml`. It is enabled in the `pyproject.toml` so it works fine if
you build from `maturin`. However I think it's useful (to prevent
confusion) to have `cargo build` when invoked on the whole workspace to
still not fail. Plus, I think it's nice you can still just run `cargo
build` even in `pumpkin-py` without needing a global package that isn't
really required under these circumstances. It also means the
dependencies in the `Cargo.toml` don't really match what you are
actually building when you use `maturin`.

There's a tiny wrinkle, though. Maybe it's the reason you removed the
feature from the `Cargo.toml` in the first place. When using workspaces
and an IDE that runs `cargo check` on save, probably the Python
interpreter will not match the one in the virtualenv that you use for
maturin. This causes PyO3 to recompile on most builds. Thankfully I've
had this problem and this can be fixed, see
PyO3/pyo3#1708. This does mean that by default
someone who will develop the python interface will need to add something
to their config manually. The alternative solution however means that
cargo build will also fail without some manual work by the user
(installing a venv to the right place).

When testing, I found that the current version of the Python script
actually fails, so I fixed that and updated the README with the correct
path to the file to run. So even if you disagree with the main change
that part is definitely useful.
  • Loading branch information
tiptenbrink authored Dec 19, 2024
1 parent d5df8f3 commit e587458
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 3 deletions.
2 changes: 1 addition & 1 deletion pumpkin-py/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ name = "pumpkin_py"
crate-type = ["cdylib"]

[dependencies]
pyo3 = "0.23.0"
pyo3 = { version = "0.23.3", features= ["extension-module"] }
pumpkin-solver = { path = "../pumpkin-solver" }
21 changes: 20 additions & 1 deletion pumpkin-py/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,24 @@ again.

Then, the examples (for example `nqueens`) can be run with
```
python python/examples/nqueens.py 5
python examples/nqueens.py 5
```

### PyO3 rebuilds

When developing in an IDE that runs `cargo check` on save, the PyO3 build
cache can get invalidated unnecessarily. See https://github.com/PyO3/pyo3/issues/1708
for more details. One way to fix this is by making `rust-analyzer` use a
different directory. In VSCode, you could fix this by adding the following
to your `.vscode/settings.json` (in the main project directory):

```json
{
"rust-analyzer.server.extraEnv": {
"CARGO_TARGET_DIR": "target/analyzer"
},
"rust-analyzer.check.extraArgs": [
"--target-dir=target/analyzer"
]
}
```
2 changes: 1 addition & 1 deletion pumpkin-py/examples/nqueens.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def main(n: int, proof: Path | None):
for row in range(n):
print(f"{row_separator}");

queen_col = solution.value(variables[row])
queen_col = solution.int_value(variables[row])

for col in range(n):
string = "| * " if queen_col == col else "| "
Expand Down

0 comments on commit e587458

Please sign in to comment.