Commit Graph

7187 Commits

Author SHA1 Message Date
Charlie Marsh 46a03b5518
Use a `DryRun` enum everywhere (#11303)
## Summary

Rather than Yet Another Bool.
2025-02-07 00:42:49 +00:00
Charlie Marsh 51c05df9c9
Use an iterator for logging upgrade events (#11301)
## Summary

No behavior changes... This just separates the formatting from the
collection of the results, and also fixes a bug whereby we didn't say
"No changes detected" in some cases.
2025-02-07 00:13:58 +00:00
Charlie Marsh 8335a6d816
Add `uv sync --dry-run` (#11299)
## Summary

Allows users to understand how the environment will change prior to
committing.

Closes https://github.com/astral-sh/uv/issues/11282.
2025-02-06 23:52:49 +00:00
Zanie Blue 67e2f904ed
Add Discord to PyPI project URLs (#11286)
Referencing
https://daniel.feldroy.com/posts/2023-08-pypi-project-urls-cheatsheet


70eac9796f/warehouse/templates/packaging/detail.html (L38-L39)
2025-02-06 16:47:23 -06:00
konsti 5493deff65
Fix marker merging for requirements.txt for psycopg (#11298)
Given an input in the shape:

```
foo[bar]==1.0.0; sys_platform == 'linux'
foo==1.0.0; sys_platform != 'linux'
```

We would write either

```
foo==1.0.0; sys_platform == 'linux'
```
or
```
foo==1.0.0
```

depending on the iteration order, as the first one is from the marker
proxy package and the second one from the package without marker.

The fix correctly merges graph entries when there are two nodes with
different extras and different markers.

I tried to write a packse test but it failed due to a different
iteration order showing the correct case directly instead of the failing
one we'd need.

Only `strip_extras` is affected, since `combine_extras` uses
`version_marker`.
2025-02-06 22:31:53 +01:00
konsti cfd1e670dd
Acquire reporter lock once (#11278)
See https://github.com/astral-sh/uv/pull/11165#discussion_r1943853482
2025-02-06 09:49:44 +00:00
samsja 5ce48cf799
add pip install setuptools in flash-attn docs (#11271)
## Summary

I followed the docs to install flash-attn with uv and got the following
issue
```uv sync
Resolved 27 packages in 12ms
  × Failed to build `flash-attn==2.7.4.post1`
  ├─▶ The build backend returned an error
  ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1)

      [stderr]
      Traceback (most recent call last):
        File "<string>", line 8, in <module>
      ModuleNotFoundError: No module named 'setuptools'

      hint: This usually indicates a problem with the package or the build environment.
```

installing setuptools before running uv sync as done with torch helps
fix it.
## Test Plan

I tested locally before it failed to install, after it worked
2025-02-05 20:59:56 -05:00
Charlie Marsh 306fcfe718
Use consistent 'Registry' prefix for wheel and source distribution logs (#11270)
## Summary

We say "Registry requirement already cached" for source distributions,
but for wheels, it's just "Requirement already cached".
2025-02-06 01:35:11 +00:00
Zanie Blue ca73c47543
Bump version to 0.5.29 (#11267) 2025-02-05 19:59:29 -05:00
Charlie Marsh b6d7adf26c
Update 3.12.8 references in Windows Python tests (#11268) 2025-02-05 19:59:19 -05:00
konsti 59b46bc216
Show messages for builds and large downloads in non-interactive mode (#11165)
When stderr is not a tty, we currently don't show any messages for build
or large downloads, since indicatif is hidden. We can improve this by
showing a message for:

* Starting and finishing a large download (>1MB)
* Starting and finishing a build

Downloads are limited to 1MB or unknown size to keep the logs concise
and not scroll the entire terminal away for a download that finishes
almost immediately.

These messages are not captured in the tests since their order is
non-deterministic (downloads and builds race to finish).

There are no "tick" messages for large downloads yet, we could e.g. show
an update on runnning downloads every n seconds.

Part of #11121

**Test Plan**

```
$ uv venv && FORCE_COLOR=1 cargo run -q pip install numpy --no-binary :all: --no-cache 2>&1 | tee a.txt
  Using CPython 3.13.0
  Creating virtual environment at: .venv
  Activate with: source .venv/bin/activate
  Resolved 1 package in 221ms
     Building numpy==2.2.2
        Built numpy==2.2.2
  Prepared 1 package in 2m 34s
  Installed 1 package in 6ms
   + numpy==2.2.2
```


![image](https://github.com/user-attachments/assets/f4b64313-afa7-449f-9e5b-2b1b7026bef3)


```
$ uv venv && FORCE_COLOR=1 cargo run -q pip install torch --no-cache 2>&1 | tee b.txt
  Using CPython 3.13.0
  Creating virtual environment at: .venv
  Activate with: source .venv/bin/activate
  Resolved 24 packages in 648ms
  Downloading setuptools (1.2MiB)
  Downloading nvidia-cuda-cupti-cu12 (13.2MiB)
  Downloading torch (731.1MiB)
  Downloading nvidia-nvjitlink-cu12 (20.1MiB)
  Downloading nvidia-cufft-cu12 (201.7MiB)
  Downloading nvidia-cuda-nvrtc-cu12 (23.5MiB)
  Downloading nvidia-curand-cu12 (53.7MiB)
  Downloading nvidia-nccl-cu12 (179.9MiB)
  Downloading nvidia-cudnn-cu12 (634.0MiB)
  Downloading nvidia-cublas-cu12 (346.6MiB)
  Downloading sympy (5.9MiB)
  Downloading nvidia-cusparse-cu12 (197.8MiB)
  Downloading nvidia-cusparselt-cu12 (143.1MiB)
  Downloading networkx (1.6MiB)
  Downloading nvidia-cusolver-cu12 (122.0MiB)
  Downloading triton (241.4MiB)
   Downloaded setuptools
   Downloaded networkx
   Downloaded sympy
   Downloaded nvidia-cuda-cupti-cu12
   Downloaded nvidia-nvjitlink-cu12
   Downloaded nvidia-cuda-nvrtc-cu12
   Downloaded nvidia-curand-cu12
[...]
```


![image](https://github.com/user-attachments/assets/71918d94-c5c0-44ce-bea8-aaba6cd80ef7)
2025-02-05 17:38:02 -06:00
Avasam b83e25a911
Update bad shell assumptions from "Shell autocompletion" page (#11202)
<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

- PowerShell isn't Windows-only
- Bash/Elvish isn't UNIX-only
- PowerShell profile won't necessarily exist
- Extracted the `echo $SHELL` tip to a tip tooltip

The new PowerShell lines are taken directly from
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7.5#how-to-create-a-profile

Note that the "Standalone installer" commands are unaffected. Running
the Linux one through PowerShell worked just fine.

## Test Plan

Look at the page generated from this PR
<!-- How was it tested? -->
2025-02-05 17:33:48 -06:00
github-actions[bot] e1fbffb9e0
Update to latest `python-build-standalone` release (#11261)
See https://github.com/astral-sh/python-build-standalone/releases/tag/20250205

---------

Co-authored-by: zanieb <2586601+zanieb@users.noreply.github.com>
Co-authored-by: Zanie Blue <contact@zanie.dev>
2025-02-05 17:31:43 -06:00
Zanie Blue c64965273f
Misc. improvements to the documentation (#11255) 2025-02-05 17:22:09 -06:00
Zanie Blue 7ec68e1dda
Allow opt-in to `.python-version` in `uv init` with `--pin-python` (#11265)
For use with `--bare`
2025-02-05 17:21:58 -06:00
Charlie Marsh 7170c5d25c
Avoid using `user_display` for workspace locks (#11264)
## Summary

Right now, the logs are like: "Acquired lock for ``"
2025-02-05 16:58:57 -05:00
Charlie Marsh 53d1a7aa6e
Always use base Python discovery logic for cached environments (#11254)
## Summary

This is attempting to solve the same problem surfaced in #11208 and
#11209. However, those PRs only worked for our own managed Pythons. In
Gentoo, for example, they disable the managed Pythons, which led to
failures in the test suite, because the "base Python" returned after
creating a virtual environment would differ from the "base Python" that
you get after _querying_ an existing virtual environment.

The fix here is to apply our same base Python normalization and
discovery logic, to non-standalone / non-managed Pythons. We continue to
use `sys._base_executable` for such Pythons when creating the
virtualenv, but when _caching_, we perform this second discovery step.

Closes https://github.com/astral-sh/uv/issues/11237.
2025-02-05 15:47:56 -05:00
Charlie Marsh c0f6406c76
Migrate to published `astral-tokio-tar` crate (#11260)
We now publish this to `crates.io`:
https://crates.io/crates/astral-tokio-tar
2025-02-05 15:43:33 -05:00
Aria Desires 72d9361ce1
fix handling of `--all-groups` and `--no-default-groups` flags (#11224)
This is a rewrite of the groups subsystem to have more clear semantics,
and some adjustments to the CLI flag constraints. In doing so, the
following bugs are fixed:

* `--no-default-groups --no-group foo` is no longer needlessly rejected
* `--all-groups --no-default-groups` now correctly evaluates to
`--all-groups` where previously it was erroneously being interpretted as
just `--no-default-groups`
* `--all-groups --only-dev` is now illegal, where previously it was
accepted and mishandled, as if it was a mythical `--only-all-groups`
flag

Fixes #10890
Closes #10891
2025-02-05 15:31:23 -05:00
Charlie Marsh 311a96bd28
Use a flock to avoid concurrent initialization of project environments (#11259)
## Summary

If you `uv run` from the same directory via multiple processes at the
same time, some of them will fail as they'll see an "incomplete" virtual
environment.

Closes https://github.com/astral-sh/uv/issues/11219.
2025-02-05 15:19:55 -05:00
Zanie Blue 239f3d76b9
Allow the project `VIRTUAL_ENV` warning to be silenced with `--no-active` (#11251)
Follow-up to https://github.com/astral-sh/uv/pull/11189

Closes https://github.com/astral-sh/uv-pre-commit/issues/36
2025-02-05 19:44:46 +00:00
Zanie Blue 2105b8a89d
Minor touchups to the Docker provenance docs (#11252) 2025-02-05 17:09:15 +00:00
Zanie Blue 1f963d1b89
Move content from the `mkdocs.public.yml` into the template (#11246)
Closes https://github.com/astral-sh/uv/issues/11242
2025-02-05 16:13:46 +00:00
Zanie Blue acbbb2b82a
Add `--bare` option to `uv init` (#11192)
People are looking for a less opinionated version of `uv init`. The goal
here is to create a `pyproject.toml` and nothing else. With the `--lib`
or `--package` flags, we'll still configure a build backend but we won't
create the source tree. This disables things like the default
`description`, author behavior, and VCS.

See

- https://github.com/astral-sh/uv/issues/8178
- https://github.com/astral-sh/uv/issues/7181
- https://github.com/astral-sh/uv/issues/6750
2025-02-05 10:12:27 -06:00
Zanie Blue 989b103171
Add support for respecting `VIRTUAL_ENV` in project commands via `--active` (#11189)
I think `UV_PROJECT_ENVIRONMENT` is too complicated for use-cases where
the user wants to sync to the active environment. I don't see a
compelling reason not to make opt-in easier. I see a lot of questions
about how to deal with this warning in the issue tracker, but it seems
painful to collect them here for posterity.

A notable behavior here — we'll treat this as equivalent to
`UV_PROJECT_ENVIRONMENT` so... if you point us to a valid virtual
environment that needs to be recreated for some reason (e.g., new Python
version request), we'll happily delete it and start over.
2025-02-05 10:12:19 -06:00
Jo 6f8d9b85d8
Remove `cachedir` dependency (#11240)
## Summary

Vendor the `HEADER` constant too so we can eliminate the dependency on
`cachedir`.
2025-02-05 08:54:02 -05:00
Charlie Marsh ee2bdc21fa
Disable wheel testing for `powerpc64le-unknown-linux-gnu` (#11229)
## Summary

I need to look into this later, but the test step is failing to install
Python:
https://github.com/astral-sh/uv/actions/runs/13148286589/job/36694160839.
We already disable this for the non-`le` variant, so this seems ok to
revisit.
2025-02-04 22:46:08 -05:00
Charlie Marsh fea00dcdd5
Bump version to v0.5.28 (#11228) 2025-02-04 20:28:43 -05:00
Charlie Marsh f615e81ad5
Clear ephemeral overlays when running tools (#11141)
## Summary

This PR removes the ephemeral `.pth` overlay when using a cached
environment. This solution isn't _completely_ safe, since we could
remove the `.pth` file just as another process is starting the
environment... But that risk already exists today, since we could
_overwrite_ the `.pth` file just as another process is starting the
environment, so I think what I've added here is a strict improvement.

Ideally, we wouldn't write this file at all, and we'd instead somehow
(e.g.) pass a file to the interpreter to run at startup? Or find some
other solution that doesn't require poisoning the cache like this.

Closes https://github.com/astral-sh/uv/issues/11117.

# Test Plan

Ran through the great reproduction steps from the linked issue.

Before:

![Screenshot 2025-01-31 at 2 11
31 PM](https://github.com/user-attachments/assets/d36e1db5-27b1-483a-9ced-bec67bd7081d)

After:

![Screenshot 2025-01-31 at 2 11
39 PM](https://github.com/user-attachments/assets/1f963ce0-7903-4acd-9fd6-753374c31705)
2025-02-04 22:45:45 +00:00
Charlie Marsh 2fad82c735
Set base executable when returning virtual environment (#11209)
## Summary

I'm not sure that this has much of an effect in practice, but currently,
when we return a virtual environment, the `sys_base_executable ` of the
parent ends up being retained as `sys_base_executable` of the created
environment. But these can be, like, subtly different? If you have a
symlink to a Python, then for the symlink, `sys_base_executable` will be
equal to `sys_executable`. But when you create a virtual environment for
that interpreter, we'll set `home` to the resolved symlink, and so
`sys_base_executable` will be the resolved symlink too, in general.
Anyway, this means that we should now have a consistent value between
(1) returning `Virtualenv` from the creation routine and (2) querying
the created interpreter.
2025-02-04 22:32:47 +00:00
Charlie Marsh 34552e2d3d
Use base Python for cached environments (#11208)
## Summary

It turns out that we were returning slightly different interpreter paths
on repeated `uv run --with` commands. This likely didn't affect many (or
any?) users, but it does affect our test suite, since in the test suite,
we use a symlinked interpreter.

The issue is that on first invocation, we create the virtual
environment, and that returns the path to the `python` executable in the
environment. On second invocation, we return the `python3` executable,
since that gets priority during discovery. This on its own is
potentially ok. The issue is that these resolve to different
`sys._base_executable` values in these flows... The latter gets the
correct value (since it's read from the `home` key), but the former gets
the incorrect value (since it's just the `base_executable` of the
executable that created the virtualenv, which is the symlink).

We now use the same logic to determine the "cached interpreter" as in
virtual environment creation, to ensure consistency between those paths.
2025-02-04 17:23:06 -05:00
Zanie Blue ec480bd3ee
Allow discovering virtual environments from the first interpreter found on the `PATH` (#11218)
Closes https://github.com/astral-sh/uv/issues/11214

Special-cases the first Python executable we find on the `PATH`,
allowing it to be considered during searches for virtual environments.

For some context, there are two stages to Python interpreter discovery

1. We find possible Python executables in various sources
2. We query the executables to determine canonical metadata about the
interpreter

We can't really be "sure" if an executable is a complaint virtual
environment during (1), we need to query the interpreter first. This
means that if you're only allowed to installed into virtual
environments, we'll query every interpreter on your PATH. This is not
performant, and causes confusion for users. Notably, I recently improved
error messaging when we can't find any valid interpreters, by showing
the error message we encounter while querying an interpreter (if any).
However, this is problematic when there's an error for an interpreter
that is not relevant to your search. In
https://github.com/astral-sh/uv/pull/11143, I added filtering to avoid
querying additional interpreters, but that regressed some user
experiences where they were relying on us finding implicitly active
virtual environments via the PATH.
2025-02-04 15:41:37 -06:00
Martijn Pieters 04374b03cc
Docs on how to verify uv docker image attestations (#11140)
As [requested by
@zanieb](https://github.com/astral-sh/uv/pull/8685#issuecomment-2627556992).
2025-02-04 15:38:19 -06:00
konsti ac1004284a
Fix hardlinks in tar unpacking (#11221)
In https://github.com/astral-sh/tokio-tar/pull/2, we accidentally
changed the `target_base` from the target base to the parent of the
file. This would cause hardlink unpacking to fail.

Example: A hardlink at `hardlinked-0.1.0/pyproject.toml` pointing to
`hardlinked-0.1.0/pyproject.toml.real` would try pointing to
`hardlinked-0.1.0/hardlinked-0.1.0/pyproject.toml.real` instead and fail
the unpacking.

The actual fix is in astral-tokio-tar, on the uv side there are only tests.

Fixes #11213
2025-02-04 17:38:22 +00:00
Charlie Marsh 748582ee6f
Disable SSL in Git commands for `--allow-insecure-host` (#11210)
## Summary

Closes https://github.com/astral-sh/uv/issues/11176.

## Test Plan

- Created a self-signed certificate.
- Ran `openssl s_server -cert cert.pem -key key.pem -WWW -port 8443`.
- Verified that `cargo run pip install
git+https://localhost:8443/repo.git` failed with:

```
error: Git operation failed
  Caused by: failed to fetch into: /Users/crmarsh/.cache/uv/git-v0/db/0773914b3ec4a56e
  Caused by: process didn't exit successfully: `/usr/bin/git fetch --force --update-head-ok 'https://localhost:8443/repo.git' '+HEAD:refs/remotes/origin/HEAD'` (exit status: 128)
--- stderr
fatal: unable to access 'https://localhost:8443/repo.git/': SSL certificate problem: self signed certificate
```

- Verified that `cargo run pip install
git+https://localhost:8443/repo.git --allow-insecure-host
https://localhost:8443` continued further.
2025-02-04 10:57:57 -05:00
konsti d9907f6fda
Update resolver internals docs (#11098)
Since the resolver internals docs were written, we added a lot more
features to the resolver, which should be documented.

As usual, these docs are not targeted at regular users, but should give
interested readers an insight into the internals of uv and help advanced
users with especially hard resolver problems.
2025-02-04 13:06:27 +00:00
konsti 1d9db68511
Move wheel helper function to wheel module (#11212)
No functional changes.
2025-02-04 12:03:38 +00:00
Matthieu Ancellin 241561979f
Fix typo "dependency-group" in error message and comments (#11211)
<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

I got a bit confused when testing `[dependency-groups]` because uv's
error message had the same typo I did in my `pyproject.toml`.
I tried to fix it, as well as a few comment I found along the way.
2025-02-04 12:16:05 +01:00
FishAlchemist 49b85d2e65
Add ``last updated`` for document (#11164)
## Summary

![image](https://github.com/user-attachments/assets/75431f9f-debe-435d-a02e-d216be7a3a01)

![image](https://github.com/user-attachments/assets/2d1b895e-4878-410e-90ff-ff8e932cbf24)
Display the last document update time, excluding any automatically
generated parts of the document, while ensuring that Google can
accurately read and recognize the webpage's time.

Note that I do not have permission to update
``requirements-insiders.txt``


Google time info
*
https://developers.google.com/search/blog/2019/03/help-google-search-know-best-date-for
*
https://developers.google.com/search/docs/appearance/structured-data/article#amp

Similar https://github.com/astral-sh/uv/pull/11162
Closes #11148
## Test Plan
uvx --with-requirements docs/requirements.txt -- mkdocs serve -f
mkdocs.public.yml --strict

![image](https://github.com/user-attachments/assets/6e8cd609-2e60-489c-97cc-fb28aa3204e0)
The correct format is actually ``2024-08-08T22:01:08Z``, but Google
Search happens to be lenient and accepts this format.

![image](https://github.com/user-attachments/assets/2ec8ce98-49ea-403b-bbd2-3d0d5630a562)
2025-02-03 22:28:47 -05:00
Zanie Blue 73e9928d40
Bump version to 0.5.27 (#11201) 2025-02-03 16:55:36 -06:00
Gregory Power f54979f2bc
add instructions for deactivating an environment (#11200)
## Summary

Add instructions for deactivating a virtual environment.

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
2025-02-03 22:16:41 +00:00
Zanie Blue bb3ffcfe52
Improve error messages for `uv pip install` with `--extra` or `--all-extras` and invalid sources (#11193)
Closes https://github.com/astral-sh/uv/issues/11190
Closes https://github.com/astral-sh/uv/issues/7845

This error message was copied over from `uv pip compile` (presumably)
but makes way more sense there than here.
2025-02-03 16:12:39 -06:00
Zanie Blue dd7cd2e86a
Remove warnings for missing lower bounds (#11195)
These are noisy relative to the effect they have on the user. It seems
better to prioritize hints on poor resolutions. Notably, it seems hard
to make these "not noisy" ref #11091.

Does not include the "lowest" resolution mode, in which lower bounds are
critical.
2025-02-03 16:03:31 -06:00
Charlie Marsh efbc77bc37
Use wire JSON schema for conflict items (#11196)
## Summary

Closes https://github.com/astral-sh/uv/issues/11180.
2025-02-03 21:22:13 +00:00
Zanie Blue 1be8ba7df1
Add best-practice flags to `pip install` example in troubleshooting guide (#11194) 2025-02-03 20:13:55 +00:00
Charlie Marsh 85461c2c90
Avoid setting permissions during tar extraction (#11191)
## Summary

As in our zip operation (and like pip), we want to explicitly avoid
setting permissions during unpacking -- apart from setting the
executable bit.

This depends on https://github.com/astral-sh/tokio-tar/pull/8.

Closes https://github.com/astral-sh/uv/issues/11188.
2025-02-03 19:29:11 +00:00
Charlie Marsh 7b43baf251
Use Astral-maintained `tokio-tar` fork (#11174)
## Summary

I shipped one security fix here along with several significant
performance improvements for large TAR files:

- https://github.com/astral-sh/tokio-tar/pull/2
- https://github.com/astral-sh/tokio-tar/pull/4
- https://github.com/astral-sh/tokio-tar/pull/5

I also PR'd the security fix to `edera-dev`
(https://github.com/edera-dev/tokio-tar/pull/4).
2025-02-03 17:51:35 +00:00
konsti 56684e4c24
Respect concurrency limits in parallel index fetch (#11182)
With the parallel simple index fetching, we would only acquire one
download concurrency token, meaning that we could in the worst case make
times the number of indexes more requests than the user requested limit.
We fix this by passing the semaphore down to the simple API method.
2025-02-03 16:41:17 +01:00
konsti c54dbcbcc2
Use dev drive for trampoline CI to avoid timeout (#11015)
Sometimes that job is just slow:
https://github.com/astral-sh/uv/actions/runs/12996921221/job/36247398606
2025-02-03 15:38:56 +01:00
konsti f7c3f30a16
Update pubgrub to set-based outdated priority tracking (#11169)
Looks like the set based prioritize tracking from
https://github.com/pubgrub-rs/pubgrub/pull/313 is a slight speedup.

I assume the changed derivation tree in the error snapshot is due to
out-of-sync virtual package priorities, while the main package priority
defining the solution remains stable.

```
$ hyperfine --warmup 2 "./uv-main pip compile --no-progress scripts/requirements/airflow.in --universal" "./uv-branch pip compile --no-progress scripts/requirements/airflow.in --universal"
  Benchmark 1: ./uv-main pip compile --no-progress scripts/requirements/airflow.in --universal
    Time (mean ± σ):     115.0 ms ±   4.8 ms    [User: 131.0 ms, System: 113.6 ms]
    Range (min … max):   108.1 ms … 125.8 ms    25 runs

  Benchmark 2: ./uv-branch pip compile --no-progress scripts/requirements/airflow.in --universal
    Time (mean ± σ):     105.4 ms ±   2.6 ms    [User: 118.5 ms, System: 113.5 ms]
    Range (min … max):   101.1 ms … 111.9 ms    28 runs

  Summary
    ./uv-branch pip compile --no-progress scripts/requirements/airflow.in --universal ran
      1.09 ± 0.05 times faster than ./uv-main pip compile --no-progress scripts/requirements/airflow.in --universal
```
2025-02-03 13:08:51 +01:00