-
Notifications
You must be signed in to change notification settings - Fork 332
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
Adds py_require()
function
#1706
base: main
Are you sure you want to change the base?
Conversation
Thanks @edgararuiz! This is coming together very nicely. I tried it out locally and also pushed some changes adding Some loose thoughts in no particular order:
But we'll want to think about how to signal a helpful error here, since both of these succeed.
py_require()
#> Declared Python Requirements ------------------------------
#> Packages: pandas, numpy, tensorflow==2.18.*
#> Python: >=3.10,<=3.12
#> Requests History ----------------------------
#> py_require("numpy") # from package:reticulate
#> py_require("tensorflow==2.18.*") # from package:tensorflow
#> py_require("tensorflow*", action = "omit") # from package:keras3
#> py_require(c("jax", "jax-metal", "keras")) # from package:keras3
#> py_require(c("pandas", "pillow")) # R_Globalenv
|
…messages default to silent
Here is the updated functionality:
library(reticulate)
py_require(python_version = "3.10")
py_require(c("pandas", "numpy"))
tf_package <- function() {
py_require(
packages = c("tensorflow==2.18.*", "keras"),
python_version = c(">=3.09", "<=3.11"),
silent = TRUE
)
}
environment(tf_package) <- asNamespace("tensorflow")
tf_package()
py_require()
#> ------------------ Python requirements ----------------------
#> Current requirements ----------------------------------------
#> Packages: numpy, pandas, tensorflow==2.18.*, keras
#> Python: >=3.09, <=3.11, 3.10
#> Non-user requirements ---------------------------------------
#> requestor packages python
#> 1 package:reticulate | numpy | |
#> 2 package:tensorflow | tensorflow==2.18.*, keras | >=3.09, <=3.11 |
reticulate:::get_or_create_venv(
requirements = reticulate:::get_python_reqs("packages"),
python_version = reticulate:::get_python_reqs("python_versions")
)
#> + /Users/edgar/.local/bin/uv run --color=always --no-project --python '>=3.09,<=3.11,==3.10' --with numpy --with pandas --with 'tensorflow==2.18.*' --with keras -
#> [1] "/Users/edgar/.cache/uv/archive-v0/1p-tXCQBAIBoD4IBM-AI8/bin/python3" Created on 2025-01-07 with reprex v2.1.1 I'll start working on the |
This looks great! I really like the new print method! |
A new action was added: "set", which only works with library(reticulate)
py_require(exclude_newer = "2024-10-20", action = "set")
py_require(python_version = "3.10")
py_require(c("pandas", "numpy"))
tf_package <- function() {
py_require(
packages = c("tensorflow==2.18.*", "keras"),
python_version = c(">=3.09", "<=3.11"),
silent = TRUE
)
}
environment(tf_package) <- asNamespace("tensorflow")
tf_package()
py_require("numpy", action = "omit")
# Fails on older date
reticulate:::get_or_create_venv(
requirements = reticulate:::get_python_reqs("packages"),
python_version = reticulate:::get_python_reqs("python_versions"),
exclude_newer = reticulate:::get_python_reqs("exclude_newer")
)
#> + /Users/edgar/.local/bin/uv run --color=always --no-project --python-preference=only-managed --exclude-newer 2024-10-20 -
#> Error in reticulate:::get_or_create_venv(requirements = reticulate:::get_python_reqs("packages"), : Python requirements could not be satisfied.
#> Call `py_require()` to omit or replace conflicting requirements.
# Succeeds on newer date
py_require(exclude_newer = "2024-12-20", action = "set")
reticulate:::get_or_create_venv(
requirements = reticulate:::get_python_reqs("packages"),
python_version = reticulate:::get_python_reqs("python_versions"),
exclude_newer = reticulate:::get_python_reqs("exclude_newer")
)
#> + /Users/edgar/.local/bin/uv run --color=always --no-project --python-preference=only-managed --exclude-newer 2024-12-20 -
#> [1] "/Users/edgar/.cache/uv/archive-v0/l3Y2P_i7JkY-qJMQfqsOF/bin/python3" Created on 2025-01-07 with reprex v2.1.1 |
Here's an example of the print out: py_require()
#> ------------------ Python requirements ----------------------
#> Current requirements ----------------------------------------
#> Packages: pandas, tensorflow==2.18.*, keras
#> Python: >=3.09, <=3.11, 3.10
#> Exclude: After 2024-10-20
#> Non-user requirements ---------------------------------------
... |
…ds first snapshot test
…at's accumulated in py_require() and adds a conditional for package error print out
…tudio/Positron, anything else uses system2
…x, improvements on the error messages, including preventing any truncation of the error message
…tep, and removes forcing the use of system2 (oops)
New error output: > devtools::load_all(".")
ℹ Loading reticulate
> get_or_create_venv(
+ paste0("package", c(1:20)),
+ python_version = c(">=3.09", "3.10"),
+ exclude_newer = "2025-01-01"
+ )
× No solution found when resolving `--with` dependencies:
╰─▶ Because there are no versions of package1 and you require package1, we
can conclude that your requirements are unsatisfiable.
-- Current requirements:-------------------------------------------------
Python: >=3.09, ==3.10
Packages: package1, package2, package3, package4, package5, package6,
package7, package8, package9, package10, package11,
package12, package13, package14, package15, package16,
package17, package18, package19, package20
Exclude: Anything newer than 2025-01-01
-------------------------------------------------------------------------
Error: Call `py_require()` to remove or replace conflicting requirements. |
Re-using the same "Current requirements" output from error, created a new table-like output to list the individual non-user environment requests, which wrap the packages and python version. The table will adapt the width based on the name of the longest environment. It separates the non-user requirements in two sections, packages and non-packages. Figured 99% of output will be from packages, so it makes it clearer about where the requirement is coming from: > devtools::load_all()
ℹ Loading reticulate
> tf_package <- function() {
+ py_require(paste0("pacakge", 1:10), python_version = "3.10")
+ }
> environment(tf_package) <- asNamespace("tensorflow")
> tf_package()
> py_require()
========================== Python requirements ==========================
-- Current requirements -------------------------------------------------
Python: 3.10
Packages: numpy, pacakge1, pacakge2, pacakge3, pacakge4, pacakge5,
pacakge6, pacakge7, pacakge8, pacakge9, pacakge10
-- R package requests ---------------------------------------------------
R package Python package(s) Python version
reticulate numpy
tensorflow pacakge1, pacakge2, pacakge3, pacakge4, 3.10
pacakge5, pacakge6, pacakge7, pacakge8,
pacakge9, pacakge10 |
> devtools::load_all()
ℹ Loading reticulate
> import("numpy")
Module(numpy)
> import("mall")
Error in py_module_import(module, convert = convert) :
ModuleNotFoundError: No module named 'mall'
Run `reticulate::py_last_error()` for details.
> py_require("mlverse-mall")
> import("mall")
Module(mall) Still need to work on adding to |
As discussed, this will be a PR focused on making sure
py_require()
"looks and acts" as expected. It does not include anyuv
integration work, it simply works out the logic for selecting and updating the Python packages and version thatuv
will prepare for the end-user.Here is a sequence of different scenarios simulating a single user initialization, which culminates in using the resulting lists to confirm that
uv run
is able to use them to return Python environment. I added in-line comments to each step to clarify the intention of the command call, and the thought process behind the whypy_require()
acted as it did:Created on 2024-12-30 with reprex v2.1.1