astral-sh / uv

An extremely fast Python package and project manager, written in Rust.
https://docs.astral.sh/uv
Apache License 2.0
26.73k stars 774 forks source link

uv run does not use env-var in .env-file #8862

Open woutervh opened 1 week ago

woutervh commented 1 week ago

uv --version uv 0.4.30 (61ed2a236 2024-11-04)

Testing .env-file support, but I can't get this to work. Using private indexes still remains painful.

> cat .env
>>>UV_EXTRA_INDEX_URL="https://__token__:<TOKEN>@gitlab.com/api/v4/groups/<PROJECT_ID>/-/packages/pypi/simple"

> uv run  --with <mypackage>   --verbose --env-file .env
DEBUG uv 0.4.30 (61ed2a236 2024-11-04)
DEBUG Read environment file at: `.env`
DEBUG No project found; searching for Python interpreter
DEBUG Searching for default Python interpreter in managed installations, system path, or `py` launcher
DEBUG Searching for managed installations at `...\uv\python`
DEBUG Found managed installation `cpython-3.12.5-windows-x86_64-none`
DEBUG Found `cpython-3.12.5-windows-x86_64-none` at `....\uv\python\cpython-3.12.5-windows-x86_64-none\python.exe` (managed installations)
DEBUG Using Python 3.12.5 interpreter at: ...\uv\python\cpython-3.12.5-windows-x86_64-none\python.exe
DEBUG At least one requirement is not satisfied in the base environment: <mypackage>
DEBUG Creating ephemeral environment
DEBUG Syncing ephemeral requirements
DEBUG Caching via interpreter: `...\uv\python\cpython-3.12.5-windows-x86_64-none\python.exe`
DEBUG Using request timeout of 30s
DEBUG Solving with installed Python version: 3.12.5
DEBUG Solving with target Python version: >=3.12.5
DEBUG Adding direct dependency: <mypackage>*
DEBUG No cache entry for: https://pypi.org/simple/<mypackage>/
DEBUG No netrc file found
DEBUG Searching for a compatible version of <mypackage> (*)
DEBUG No compatible version found for: <mypackage>
  × No solution found when resolving `--with` dependencies:
  ╰─▶ Because <mypackage> was not found in the package registry and you require <mypackage>, we can conclude that your requirements are unsatisfiable.
zanieb commented 1 week ago

We don't read configuration for uv itself from .env files, the variable are passed to the subcommand called by uv run

laermannjan commented 6 days ago

Hooking on to this, since I'm also testing .env support and am experiencing inconsistencies. I'm not sure whether I might be misunderstanding this feature.

$ uv version
uv 0.5.1 (Homebrew 2024-11-08)

From the documentation and https://github.com/astral-sh/uv/pull/8263 I expected that uv run [COMMAND] would export the env vars from <project_root>/.env if that file exists and pass them to the [COMMAND] (I assume that this command can be any shell command).

I've created a fresh project, added a .env and a simple script:

uv init test

cd test

cat <<EOF > .env
FOO=bar
EOF

cat <<EOF > test.py
import os
print(os.environ.get('FOO'))
EOF

The steps I've taken that I had issues with

  1. uv run -- echo $FOO displays an empty line
  2. uv run --env-file .env -- echo $FOO displays an empty line (contrary to what the docs indicate here at https://docs.astral.sh/uv/configuration/files/#env
  3. uv run test.py displays None
  4. uv run --env-file .env test.py displays bar as I would have expected for all of the above

Furthermore, I tried adding a private index (which is what led me down this path) by adding to my pyproject.toml

dependencies = [
    "secret-project==1.2.3"
]

[[tool.uv.index]]
name = "secret-project"
url = "https://secret-project.com/simple"

and the credentials to my .env:

UV_INDEX_SECRET_PROJECT_USERNAME=myusername
UV_INDEX_SECRET_PROJECT_PASSWORD=mypassword

(as the previous steps created the venv I deleted it and cleaned the cache before moving on: rm -rf .venv && uv cache clean)

  1. uv sync fails with a error: Failed to prepare distributions caused by 401 authentication error when trying to fetch secret-project - I would have really liked this to work out of the box, but expected this to fail due to how the .env support was described.
  2. uv run uv sync and uv run -- uv sync both fail for the same reason as 1.. which I didn't expect.
  3. uv run fails for the same reason as 1. and 2.
  4. uv run --env-file .env creates the venv successfully and install secret-project from the private index. Note that if you had run uv run --env-file .env -- echo $UV_INDEX_SECRET_PROJECT_USERNAME instead, it would have also successfully created the venv, but not display the env var.

So it seems to me that the env vars aren't passed to the subprocess as described and uv run doesn't load .env by default. Also it seems I cannot install/update my deps with uv sync in my case, but only with the workaround of using uv run --env-file .env to leverage it's automatic syncing.

Additionally, I would have expected uv sync to respect .env as well. Since I am defining the index in the project's pyproject.toml, I would assume that most people would like to define their credentials in the .env along with other their tokens. Just my two cents.

Please, let me know if what I described above is actually working as intended and I'm just misusing uv.

charliermarsh commented 6 days ago

Thanks for your comment. A few clarifications:

  1. uv run --env-file .env -- echo $FOO can never work -- it's a mistake in the docs. Your shell evaluates $FOO there before it's passed to uv, so it's just not possible for uv to replace it in that context.
  2. We don't load .env files by default. You must pass --env-file. (Do the docs say we read them by default somewhere?)
  3. The .env file only affects the command that uv run runs. It doesn't affect uv itself. We may expand that in the future, but the intent in this initial implementation is that uv itself shouldn't be configured by .env files. So things like UV_INDEX_SECRET_PROJECT_PASSWORD wouldn't have any effect, but something like configuring your database port via a PORT variable would.
laermannjan commented 5 days ago

Thanks for the quick response.

We don't load .env files by default. You must pass --env-file. (Do the docs say we read them by default somewhere?)

In https://github.com/astral-sh/uv/pull/8263 which is referenced in the changelog it says:

The behaviour is as follows:

  1. uv run file.py executes file.py using the .env (if it exists).
  2. [...]
  3. uv run --no-env-file file.py skips reading the .env file.

The docs say:

To disable dotenv loading (e.g., to override UV_ENV_FILE or the --env-file command-line argument), set the UV_NO_ENV_FILE environment variable to 1, or pass the--no-env-file flag to uv run.

Which I interpreted as loading is enabled by default, so I can disable it like this, but I realize I probably didn't read that sentence carefully enough.

And in the linked PR's thread there are talks about this being an opt-out feature.

So all of this made me assume it would load .env files by default and I had to opt-out via UV_NO_ENV_FILE or --no-env-file. Having to specify --env-file or UV_ENV_FILE makes it opt-in, doesn't it?


The .env file only affects the command that uv run runs. It doesn't affect uv itself. We may expand that in the future, but the intent in this initial implementation is that uv itself shouldn't be configured by .env files. So things like UV_INDEX_SECRET_PROJECT_PASSWORD wouldn't have any effect, but something like configuring your database port via a PORT variable would.

Thanks for clarifying. Having read this, I still don't understand why uv run --env-file .env -- uv sync wouldn't work, isn't uv sync the command being run by uv run and should be affected by the .env loading? Or put differently, why would uv run's auto-sync work, but not the one invoked by me manually in uv run --env-file .env -- uv sync?

charliermarsh commented 5 days ago

Thanks @laermannjan. So I think the docs are generally correct -- #8263 initially merged with the behavior as opt-out, but we changed it to opt-in prior to releasing. I can see how it's confusing.

Having read this, I still don't understand why uv run --env-file .env -- uv sync wouldn't work...

I think I would expect this to work. Let me try it.

charliermarsh commented 5 days ago

In my testing uv run --env-file .env -- uv sync --verbose does work as expected. I set UV_INDEX_URL=https://test.pypi.org in .env, and the verbose logging indicates that it read from the Test PyPI.