Skip to content

Conversation

jaimergp
Copy link
Contributor

This should result in (way) faster plugin installs.

Copy link

codecov bot commented Aug 11, 2025

Codecov Report

❌ Patch coverage is 96.38554% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.28%. Comparing base (b3908f6) to head (0e44be1).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...napari_plugin_manager/base_qt_package_installer.py 93.87% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #172      +/-   ##
==========================================
+ Coverage   94.16%   94.28%   +0.12%     
==========================================
  Files          14       14              
  Lines        2159     2222      +63     
==========================================
+ Hits         2033     2095      +62     
- Misses        126      127       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@brisvag
Copy link
Contributor

brisvag commented Aug 29, 2025

Uhhhh I didn't see this, awesome!

Copy link
Contributor

@brisvag brisvag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I'm not clear on: uv is now a dependency, in what case would it be not available?

@jaimergp
Copy link
Contributor Author

One thing I'm not clear on: uv is now a dependency, in what case would it be not available?

Almost never, but if the user manages to break their environment by removing uv for some reason, we handle that with a fallback. Also we need to note that depending on uv adds it to the napari env, which means that our uv would shadow the system one (if present) when the virtual/conda environment is active, and I'm not sure we are 100% onboard with that.

Copy link
Contributor

@Czaki Czaki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jaimergp Could you make a separate PR with rename enum from PIP to PIPI?

We may merge it fast and make this diff much cleaner.

Comment on lines 69 to 73
def constraints() -> Sequence[str]:
"""
Version constraints to limit unwanted changes in installation.
"""
return [f'napari=={_napari_version}']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The uv pip install --upgrade upgrade not only itself but also all its dependencies. But do not think about other packages installed in the environment.

So if a package depends on NumPy and Numba is installed, having only pinned napari means that the upgrade may end with a non-working environment, as import of Numba crashes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, do you think we could use --upgrade-package instead or a different --resolution strategy?

$ uv pip install --help
[...]
  -U, --upgrade                            Allow package upgrades, ignoring pinned versions in any existing output file. Implies
                                           `--refresh`
  -P, --upgrade-package <UPGRADE_PACKAGE>  Allow upgrades for a specific package, ignoring pinned versions in any existing output
                                           file. Implies `--refresh-package`
      --resolution <RESOLUTION>            The strategy to use when selecting between the different compatible versions for a given
                                           package requirement [env: UV_RESOLUTION=] [possible values: highest, lowest,
                                           lowest-direct]
[...]

And for clarity, does this affect pip install --upgrade too (without uv)?

Copy link
Contributor Author

@jaimergp jaimergp Aug 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or is the suggestion to extend the constraints() list with all the dependencies of the currently installed napari version, and possibly all the plugins? Something like:

constraints = [f"napari=={napari.__version__}, *napari_plugins(exact_version=True)]
for constraint in constraints:
    constraints.extend(get_dependencies(name_of(constraint), with_version_ranges=True)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I remember, this is what Charlie advertised to me to use: --upgrade-package.

And for clarity, does this affect pip install --upgrade too (without uv)?

As far as I know, yes. Pip, do not update all dependencies of updated packages if the lower bound is below the currently installed version. (same for --upgrade-package).

Or is the suggestion to extend the constraints() list with all the dependencies of the currently installed napari version, and possibly all the plugins? Something like:

No. As it may block many updates when a plugin requires a newer version of its dependency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I remember, this is what Charlie advertised to me to use: --upgrade-package.

Ok, I'll change to using that flag!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know, yes. Pip, do not update all dependencies of updated packages if the lower bound is below the currently installed version. (same for --upgrade-package).

This is what I can find in the help:

  -U, --upgrade               Upgrade all specified packages to the newest available version. The handling
                              of dependencies depends on the upgrade-strategy used.
  --upgrade-strategy <upgrade_strategy>
                              Determines how dependency upgrading should be handled [default: only-if-
                              needed]. "eager" - dependencies are upgraded regardless of whether the
                              currently installed version satisfies the requirements of the upgraded
                              package(s). "only-if-needed" -  are upgraded only when they do not satisfy
                              the requirements of the upgraded package(s).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Czaki any word here?

@psobolewskiPhD
Copy link
Member

Also we need to note that depending on uv adds it to the napari env, which means that our uv would shadow the system one (if present) when the virtual/conda environment is active, and I'm not sure we are 100% onboard with that.

So if someone had a system uv and they were used to using various uvx things then suddenly they'd be using uv from napari rather than their system uv? Would this create issues? Is there a way we could use uv programmatically, but not actually expose the binary?

@jaimergp
Copy link
Contributor Author

So if someone had a system uv and they were used to using various uvx things then suddenly they'd be using uv from napari rather than their system uv?

In a shell with the napari env active, or in the napari console, yes.

Is there a way we could use uv programmatically, but not actually expose the binary?

I need to look into how it is packaged, but my intuition tells me that we would only be able to do that by not depending on it in pyproject.toml, but handling the download/install ourselves (e.g. an internal environment?) or by vendoring it (ew).

@jaimergp
Copy link
Contributor Author

jaimergp commented Aug 29, 2025

Could you make a separate PR with rename enum from PIP to PIPI?

We amy merge it fast and make this diff much cleaner.

See #176. Once that's in I'll rebase here.

@jaimergp jaimergp mentioned this pull request Aug 30, 2025
@jaimergp jaimergp requested a review from Czaki September 2, 2025 08:28
@dalthviz
Copy link
Member

Hi there, so gave this a check on Windows and, as long as you install uv on the napari env, seems like things are working as expected 👍 However, just to be sure, the idea here is to always have uv available from the napari env (making it a dependency), right?

@jaimergp
Copy link
Contributor Author

jaimergp commented Oct 2, 2025

the idea here is to always have uv available from the napari env (making it a dependency), right?

Yes, it's added as a dependency in pyproject.toml.

Copy link
Contributor

@willingc willingc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @jaimergp. I think we should add pixi as well since it creates a nicer experience when using GPUs. It doesn't need to be this PR. 😄

@Czaki
Copy link
Contributor

Czaki commented Oct 2, 2025

If we add uv to dependencies, why keep the pip backend?

@jaimergp
Copy link
Contributor Author

jaimergp commented Oct 2, 2025

I'd like to keep it for a while in case it helps with debugging, workarounds, and other annoying tasks in case uv reveals some rough edges we didn't know about.

@Czaki
Copy link
Contributor

Czaki commented Oct 2, 2025

I check some uv documentation. I'm not a fan of adding it as a default dependency, as it will shadow the global one and need separate updates.

@jaimergp
Copy link
Contributor Author

jaimergp commented Oct 2, 2025

I think we should add pixi as well since it creates a nicer experience when using GPUs.

I'd like to do that too, but the inability to specify an arbitrary environment location will make integration with the installation root problematic. I can think of hacky workarounds (e.g. having an empty project folder somewhere where we maintain the necessary lockfiles and then using pixi-install-to-prefix to link the packages)

@jaimergp
Copy link
Contributor Author

jaimergp commented Oct 2, 2025

I check some uv documentation. I'm not a fan of adding it as a default dependency, as it will shadow the global one and need separate updates.

We can go back to the behaviour of only using the uv backend if it's in PATH, or auto-install it on first use to a private, non-PATH location.

@jaimergp
Copy link
Contributor Author

jaimergp commented Oct 2, 2025

Added it as an extra so folks can install it with napari-plugin-manager[uv].

@Czaki
Copy link
Contributor

Czaki commented Oct 2, 2025

Added it as an extra so folks can install it with napari-plugin-manager[uv].

You need to add it also to testing dependencies.

Copy link
Contributor

@Czaki Czaki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks really good. A few small questions.


deps =
napari_repo: git+https://github.com/napari/napari.git
extras = testing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no skip for uv tests I prefer to add uv to testing extras.

Using napari-plugin-manager[uv] in testing extras.

"""
Version constraints to limit unwanted changes in installation.
"""
return [f'napari=={_napari_version}']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. So the only risk for now is if the environment has Numba installed and the plugin needs a NumPy newer than the installed one. But it is a problem as well for pip.

Comment on lines +356 to 358
if tool == NapariPipInstallerTool
else '_python_executable',
lambda *a: 'not-a-real-executable',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we can not unify this?

Comment on lines +82 to +87
if tool == NapariPipInstallerTool:
monkeypatch.setattr(
NapariPipInstallerTool,
'origins',
('https://pypi.org/simple',),
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this if is needed? Why we cannot uify this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants