-
Notifications
You must be signed in to change notification settings - Fork 645
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
Add bootstrapping and isolation of development Python versions #1105
Conversation
6e72219
to
f3a9802
Compare
# Conflicts: # scripts/scenarios/update.py
f3a9802
to
787b8ee
Compare
# Conflicts: # crates/puffin-interpreter/src/python_query.rs # crates/puffin/src/commands/venv.rs
dfb9bf0
to
6b0f69d
Compare
pub fn find_executable<R: AsRef<OsStr> + Into<OsString> + Copy>( | ||
requested: R, | ||
) -> Result<PathBuf, Error> { | ||
if let Some(isolated) = std::env::var_os("PUFFIN_PYTHON_PATH") { | ||
if let Ok(cwd) = std::env::current_dir() { | ||
which::which_in(requested, Some(isolated), cwd) | ||
.map_err(|err| Error::Which(requested.into(), err)) | ||
} else { | ||
which::which_in_global(requested, Some(isolated)) | ||
.map_err(|err| Error::Which(requested.into(), err)) | ||
.and_then(|mut paths| paths.next().ok_or(Error::PythonNotFound)) | ||
} | ||
} else { | ||
which::which(requested).map_err(|err| Error::Which(requested.into(), err)) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to improve the error handling here in a follow-up
pub fn find_executable<R: AsRef<OsStr> + Into<OsString> + Copy>( | ||
requested: R, | ||
) -> Result<PathBuf, Error> { | ||
if let Some(isolated) = std::env::var_os("PUFFIN_PYTHON_PATH") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I worry about this name being too close to PYTHONPATH
which has a very different meaning.
Considering PUFFIN_PYTHON_EXECUTABLE_PATH
but geesh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm okay with PUFFIN_PYTHON_EXECUTABLE_PATH
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll address this once I merge this full stack to avoid annoying rebases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"path" is so overloaded anyway already
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very impressive.
scripts/bootstrap/fetch-versions
Outdated
"pgo+lto", | ||
"lto", | ||
"pgo", | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One question though - does this download one interpreter per version? Or multiple? How does it choose which one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This just generates the version.json
file which has links to downloads. I believe the flavor preferences section here is used to select a single interpreter flavor per combination of OS, architecture, and Python version.
scripts/bootstrap/install
Outdated
rm "$this_dir/$filename" | ||
done | ||
|
||
echo "Done!" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know that I feel qualified to review this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as you test it I'm not too worried about the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The install script errors on windows 11 git bash:
$ scripts/bootstrap/install.sh
which: no grealpath in (/mingw64/bin:/usr/bin:/c/Users/Konstantin/bin:/c/Python37/Scripts:/c/Python37:/c/Program Files (x86)/Intel/iCLS Client:/c/Program Files/Intel/iCLS Client:/c/Windows/system32:/c/Windows:/c/Windows/System32/Wbem:/c/Windows/System32/WindowsPowerShell/v1.0:/c/Program Files (x86)/Intel/Intel(R) Management Engine Components/DAL:/c/Program Files/Intel/Intel(R) Management Engine Components/DAL:/c/Program Files (x86)/Intel/Intel(R) Management Engine Components/IPT:/c/Program Files/Intel/Intel(R) Management Engine Components/IPT:/c/Program Files/Intel/WiFi/bin:/c/Program Files/Common Files/Intel/WirelessCommon:/c/Windows/System32/OpenSSH:/cmd:/c/Program Files/nodejs:/c/ProgramData/chocolatey/bin:/c/Program Files/PowerShell/7:/c/Users/Konstantin/AppData/Local/Programs/Python/Python312/Scripts:/c/Users/Konstantin/AppData/Local/Programs/Python/Python312:/c/Users/Konstantin/AppData/Local/Programs/Python/Python38/Scripts:/c/Users/Konstantin/AppData/Local/Programs/Python/Python38:/c/Users/Konstantin/.cargo/bin:/c/Users/Konstantin/AppData/Local/Microsoft/WindowsApps:/c/Users/Konstantin/AppData/Local/JetBrains/Toolbox/scripts:/c/users/konstantin/.local/bin:/c/Users/Konstantin/AppData/Roaming/npm:/c/Users/Konstantin/AppData/Local/Microsoft/WindowsApps:/c/Users/Konstantin/AppData/Local/Microsoft/WinGet/Packages/BurntSushi.ripgrep.MSVC_Microsoft.Winget.Source_8wekyb3d8bbwe/ripgrep-14.1.0-x86_64-pc-windows-msvc:/c/Users/Konstantin/AppData/Local/Microsoft/WinGet/Packages/jqlang.jq_Microsoft.Winget.Source_8wekyb3d8bbwe)
Installing cpython-3.8.12-mingw64_nt-10.0-22621-x86_64
No matching download for cpython-3.8.12-mingw64_nt-10.0-22621-x86_64
Then add the Python binaries to your path: | ||
|
||
``` | ||
export PATH=$PWD/bin:$PATH | ||
``` | ||
|
||
We also strongly recommend setting the `PUFFIN_PYTHON_PATH` variable during development; this will prevent your | ||
system Python versions from being found during tests: | ||
|
||
``` | ||
export PUFFIN_PYTHON_PATH=$PWD/bin | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be something like
PATH=$PWD/bin:$PATH PUFFIN_PYTHON_PATH=$PWD/bin cargo nextest run ...
Seems cumbersome to always having to copy those two commands before doing anything when not using direnv
pub fn find_executable<R: AsRef<OsStr> + Into<OsString> + Copy>( | ||
requested: R, | ||
) -> Result<PathBuf, Error> { | ||
if let Some(isolated) = std::env::var_os("PUFFIN_PYTHON_PATH") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"path" is so overloaded anyway already
.map(str::parse::<u8>) | ||
.collect::<Result<Vec<_>, _>>(); | ||
if let Ok(versions) = versions { | ||
// `-p 3.10` or `-p 3.10.1` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function docstring needs to be updated
@@ -0,0 +1,101 @@ | |||
#!/usr/bin/env bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This script introduces a jq dependency which needs to be documented. For windows:
winget install jqlang.jq
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally, I'm a fan of explicitly checking for dependencies used in a shell script if any outside of core tools are needed. For example, see https://github.com/BurntSushi/dotfiles/blob/2431b0d87614eabc0f022fd3617dcb0e5a5b4de4/bin/backup#L315-L322 and https://github.com/BurntSushi/dotfiles/blob/2431b0d87614eabc0f022fd3617dcb0e5a5b4de4/bin/backup#L341.
It's a big quality of life improvement because it avoids the scenario of getting half-way through execution and then getting a "jq
not found" error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also write this in Python to avoid adding new dependencies. And it might work on Windows. (Although I see some symlinking below...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah thanks missed that dependency in my list and I'm happy to add explicit checks.
I wrote the whole thing in Python first at #1068 — but it was annoying that it had more Python requirements and I really didn't like bootstrapping Python development with Python.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Does this mean I don't need pyenv
any more?
@@ -0,0 +1,3 @@ | |||
PATH=$PWD/bin:$PATH | |||
export PUFFIN_PYTHON_PATH=$PWD/bin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these variables be quoted?
(It would be odd for these to require quoting on a Unix system, but if this gets used on Windows, spaces are much more common there IIRC.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about the envrc syntax, but probably.
.github/workflows/ci.yml
Outdated
run: cargo nextest run --workspace --all-features --status-level skip --failure-output immediate-final --no-fail-fast -j 12 | ||
run: | | ||
export PATH=$PWD/bin:$PATH | ||
export PUFFIN_PYTHON_PATH=$PWD/bin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use direnv here instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure
""" | ||
) | ||
_suffix_re = re.compile( | ||
r"""(?x)^(.*?)-(%s)$""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can just be "-(%s)$"
, although you would need to use _suffix_re.search(...)
instead of _suffix_re.match(...)
below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is copied directly from Rye, hesitant to change?
@@ -0,0 +1,101 @@ | |||
#!/usr/bin/env bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally, I'm a fan of explicitly checking for dependencies used in a shell script if any outside of core tools are needed. For example, see https://github.com/BurntSushi/dotfiles/blob/2431b0d87614eabc0f022fd3617dcb0e5a5b4de4/bin/backup#L315-L322 and https://github.com/BurntSushi/dotfiles/blob/2431b0d87614eabc0f022fd3617dcb0e5a5b4de4/bin/backup#L341.
It's a big quality of life improvement because it avoids the scenario of getting half-way through execution and then getting a "jq
not found" error.
@@ -0,0 +1,101 @@ | |||
#!/usr/bin/env bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also write this in Python to avoid adding new dependencies. And it might work on Windows. (Although I see some symlinking below...)
scripts/bootstrap/install.sh
Outdated
|
||
# Determine system metadata | ||
os=$(uname | tr '[:upper:]' '[:lower:]') | ||
arch=$(arch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
arch
isn't a standard command. (It's not on my system for example.) I think it is Debian specific. uname --machine
might be what you're looking for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 weird its present on macOS and Ubuntu but can change
I plan on addressing this round of feedback then merging since I have a stack on top of this and we really need to codify these versions. The
That's the goal! |
…rsions (#1106) In #1040 we broke the pip compile scenarios designed to test failure when a required Python version is not available — resolution succeeded because all of the Python versions were available in CI. Following #1105 we have the ability to isolate tests from Python versions available in the system. Here, we limit the scenarios to only the Python version in the current environment, restoring our ability to test the error messages. With astral-sh/packse#95, we will be able to specify scenarios with access to additional system Python versions. This will allow us to include test coverage where resolution can succeed by using a version available elsewhere on the system. See #1111 for this follow-up.
#1131 shows that `direnv` installation is _most_ of the CI overhead introduced by #1105. Instead of using `direnv`, let's just use a simple `.env` file that can be loaded with `source` or [`direnv`'s `dotenv` directive](https://direnv.net/man/direnv-stdlib.1.html#codedotenv-ltdotenvpathgtcode).
Replaces #1068 and #1070 which were more complicated than I wanted.
.python-versions
file which defines the Python versions needed for developmentscripts/bootstrap/install
which installs the required Python versions frompython-build-standalone
to./bin
versions.json
file with metadata about available versions on each platform and afetch-version
Python script derived fromrye
for updating the versionssetup-python
actionPUFFIN_PYTHON_PATH
variable to prevent lookup of system Python versions for isolation during developmentTested on Linux (via CI) and macOS (locally) — presumably it will be a bit more complicated to do proper Windows support.