Closed charliermarsh closed 1 month ago
I was thinking that we’d remove the current set_directory call, and then pass around a project directory as dictated by the directory flag.
Do you have any timeline for this change by any chance? I'm currently using the hidden --directory
option but it would be great if it can be made public (and ideally add an environment variable for it). Also resolving the paths relative to the passed directory is causing very weird issues in different places. I think this change is the correct choice.
One example where paths relative to the current directory are necessary is when running uv commands from pre-commit local hooks, paths of the files staged for commit are automatically passed to the hook entry (uv command in this case) and the user cannot control this behavior.
Thanks for uv it is looking great!
per https://github.com/astral-sh/uv/issues/6733#issuecomment-2315983710 our existing behavior matches the Git and Cargo CLIs so we're very unlikely to change it.
The pre-commit example is great though. How do people work around this in other tools? Why do you need to use --directory
in this situation? Maybe we need an environment variable to toggle.
Thanks for the quick reply. In our case, the simplified project structure was briefly something like:
my-project/
| app/
| | main.py
| | pyproject.toml
| docker/
| docs/
| bin/
| docker-compose.yml
| .pre-commit.config.yaml
We also have a local hook for mypy something like:
- repo: local
hooks:
- id: mypy
name: run mypy check
language: system
entry: uv run --directory app -- mypy
types_or: [python, pyi]
require_serial: true
args:
- --config-file=pyproject.toml
We want to run the mypy command from the virtual environment that has all the python dependencies so that mypy can resolve and follow all the imports when running the type checks.
Because of the behavior of --directory
we are passing --config-file
as pyproject.toml
instead of app/pyproject.toml
but that's okay since we can control it.
However, when the commit hook is triggered by pre-commit, we have less control.
Let's say app/main.py
was changed, the command executed by pre-commit will be something like:
uv run --directory app -- mypy --config-file=pyproject.toml app/main.py
And since the paths are resolved relative to the --directory
param, mypy won't find a module matching app/app/main.py
.
In our case, we managed to get around it by moving the pyproject.toml
to the root directory which is probably a more suitable place. However, there might be similar cases that are a bit harder to avoid.
Btw, I'm not sure if it helps, but we are in the process of migrating from poetry to uv. I can confirm that in the case of poetry, the behavior is not similar to cargo or git. It resolves paths based on the working directory.
For example, this is the same hook I mentioned in my previous comment, before the migration to uv:
- repo: local
hooks:
- id: mypy
name: run mypy check
language: system
entry: poetry --directory app run mypy
types_or: [python, pyi]
require_serial: true
args:
- --config-file=app/pyproject.toml
Notice how despite the --directory
param, we used to pass the --config-file
relative to the working directory not /app
.
I guess both options have good arguments. A toggle like you suggested might help.
@zanieb Unlikely to implement this issue? What do you think about adding a separate flag to uv run with the behaviour intended by this issue?
If we again look at git it has git -C x --git-dir y and maybe this could be a good model.
Uv can have one flag that changes directory, like -C and one flag that points out which project or pyproject.toml to discover and use as starting point. For convenience, this second project flag should also take a directory value.
Of course in the normal case these are linked and can be used for similar tasks.
Expanding on the comment from bluss -- What about adding --project
, which doesn't change the current directory, but simply indicates where to pick up the environment (or its specification)?
One more idea: if --project
references a script with inline metadata, this script would be used as the spec for the enironment to use.
For example:
uv python --project my_script_with_metadata.py find
would create and report the ephemeral interpreter which would be used for running this scriptuv tree --project my_script_with_metadata.py
would list the packages in this ephemeral env. This would a good alternative for uv tree --script my_script.py
, which I proposed under #7328I would use both of these forms for adding uv
mode to Thonny IDE.
I am open to a dedicated --project
flag on the uv run
, uv sync
, uv lock
, etc. to inform workspace discovery. \cc @zanieb
We are also very interested in a --project
argument where the paths can remain relative to the current working directory. Our use-case is a monorepo where we are unable to use the workspace feature of uv
due to conflicting dependencies between projects. This is the exact use of workspaces that is discouraged in the uv
docs today.
(Another solution for us would be if uv
had a feature where one could define a workspace without a shared uv.lock
, but I think the --project
argument sounds like a general solution that could solve more issues overall).
When I take a first look at this, I think --project
in uv run --project
maybe needs to be a global argument as well? (Or run-specific but handled early.) Because this logic is global (happens before any subcommand) and it is about project/workspace discovery. By the simplest logic: currently it's assumed project root is CWD (or found from CWD), the updated code needs to start with the user provided project directory instead of CWD here.
I'm also willing to have --project
— though Charlie said it'd be a pain to add support :)
I think project is pretty straightforward since it’s limited in scope as to what it affects. I assume it affects project discovery and maybe python-version?
I'll do it.
Epic. Sounds good to me. The python-version
file stuff might be easier with https://github.com/astral-sh/uv/pull/6370
Thanks for implementing this! It works perfectly for our needs :)
That's great to hear :)
Uv run's external command is opaque (in meaning), so it's not possible to translate those arguments. One part of the solution for uv run could be either to never change current directory, or set cwd back to the original for the child process.