GH Python3 Dependency Versions Behavior?

Hi,

I wonder what the expected behavior is when multiple Grasshopper Python3 components try and pip-install dependencies with different versions? How should we anticipate that the system will behave in that case?

For instance, what if I have a GH definition that includes one component like:

# r: pydantic==1.10
import pydantic
print(pydantic.VERSION)

and then another component in the same Grasshopper definition which includes:

# r: pydantic==2.4
import pydantic
print(pydantic.VERSION)

How would the system work in that case? Would the ‘last’ component to run win and get its version installed? Or would the second component be ignored and the first version installed gets preserved?


From my own tests, I get some slightly odd behavior, which is why I wonder how it is ‘supposed’ to work?

Example 1:

If I install v1.10, it installs v1.10. That’s good

Example 2:

If I install v2.4, it installs v2.4. Also good.

Example 3:

However, if I install v1.10 first, then install v2.4, it appears to install v2.4 within the site-env directory, but the interpreter continues to show v1.10:

Is this maybe just a ‘reload’ issue do you think?

But mostly I would just like to understand how its ‘supposed’ to work so we can anticipate that and develop towards that behavior?

thanks!
@ed.p.may

I would try using #: env to control the isolation and installs, but if sys.modules is shared between CPython components like the Iron Python ones, I would create shell wrapper libraries with a different name for any non standard versions.

However this will rapidly lead to a situation where there’s little advantage in using the automagical #r: syntax and pip, and is likely to make it worse, as you’re not in control of what’s being imported and installed, nor where.

My two pence is: Running multiple library versions together is a terrible idea that will inevitably lead to dependency hell (the whole reason venvs etc were invented in mainstream Pythons).

Getting multiple versions to work together to their satisfaction is the user’s problem. Devs should feel under no obligation to support this.

There is a discussion on environments here too:

Interesting, thank you @scottd ,

So am I correct to say there are currently 5 ways to manage dependencies for the new Python3 Grasshopper Components?

1. # r: my-package-name

  • This will pip-install the listed dependency into the .rhinocode/py39-rh8/site-envs/

2. # env: path_to_an_existing/venv/lib/python3.9/site-packages

  • This will allow for loading packages from an existing virtual-environment
  • Including a # r:... after this line will still pip-install the listed dependencies into the .rhinocode/py39-rh8/site-envs/
  • To install dependencies to this existing venv, the user just needs to use the normal terminal commands outside rhino.

3. # venv: path_to_a_new/venv

  • This will create a new virtual-environment every time Grasshopper is started.
  • This venv will have a random-string suffix on the name
  • Including a # r:... after this line will pip install the listed dependencies into this new environment, rather than into the .rhinocode/...
  • This virtual-environment is not removed or cleanup when Grasshopper is exited and it is up to the user to remove any old venvs

4. Use the Install Packages Button

  • From within the Python3 Editor, click the Install Packages icon
  • This will add the packages to the .rhinocode/... environment (?)

5. Setup a custom path to an environment using the Editor Options

  • From within the Python3 Editor, go to Tools/Options/Python3 and set a custom path
  • This path may, or may not be a python venv - it is up to the user to configure

am I stating all of that correctly?

thanks!
@ed.p.may

Number 3) is a only a ‘venv’. It’s a great tool for users. It is not provide isolation like actual venvs. Nor is it a fool proof way of running multiple versions of a single dependency together. I would advise renaming this feature.

Until the fundamental architecture is changed, in Grasshopper, everything is in a single shared. The ‘venv’ isolation only exists for the first component to import a module. After the first version of a module is imported, that version is cached in sys.modules, where all other components that also want to import it will get it from.