Skip to content

Add support to activate environments with Pixi #23558

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
seembha opened this issue Jan 26, 2025 · 8 comments · May be fixed by #23919
Open

Add support to activate environments with Pixi #23558

seembha opened this issue Jan 26, 2025 · 8 comments · May be fixed by #23919

Comments

@seembha
Copy link

seembha commented Jan 26, 2025

I am using pixi to manage my python projects and have a global environment where I want to install spyder and then use that with local project environments. Here's my setup

  • Global environment
PS C:\Users\seembha\Projects\Python\test> pixi global list
Global environments as specified in 'C:\Users\seembha\.pixi\manifests\pixi-global.toml'
└── global
    ├─ dependencies: spyder 6.0.3, jupyterlab 4.3.4, pixi-kernel 0.5.4
    └─ exposes: spyder, jupyter-lab, jupyter-labhub
  • Local environment
PS C:\Users\seembha\Projects\Python\test> pixi info
System
------------
       Pixi version: 0.40.3
           Platform: win-64
   Virtual packages: __win=0=0
                   : __archspec=1=skylake
          Cache dir: C:\Users\seembha\AppData\Local\rattler\cache
       Auth storage: C:\Users\seembha\.rattler\credentials.json
   Config locations: No config files found

Global
------------
            Bin dir: C:\Users\seembha\.pixi\bin
    Environment dir: C:\Users\seembha\.pixi\envs
       Manifest dir: C:\Users\seembha\.pixi\manifests\pixi-global.toml

Project
------------
               Name: test
            Version: 0.1.0
      Manifest file: C:\Users\seembha\Projects\Python\test\pyproject.toml
       Last updated: 24-01-2025 22:45:33

Environments
------------
        Environment: default
           Features: default
           Channels: conda-forge
   Dependency count: 2
       Dependencies: python, ipykernel, spyder-kernels
  PyPI Dependencies: test
   Target platforms: win-64
  • Current Behavior

When I use Spyder in my local environment
PS C:\Users\seembha\Projects\Python\test> pixi run spyder
the IPython console shows the following error

An error occurred while starting the kernel
The error is:

Traceback (most recent call last):
File "C:\Users\seembha\.pixi\envs\global\Lib\site‑packages\spyder\plugins\ipythonconsole\widgets\main_widget.py", line 1719, in create_new_client
kernel_handler = self.get_cached_kernel(kernel_spec, cache=cache)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\seembha\.pixi\envs\global\Lib\site‑packages\spyder\plugins\ipythonconsole\widgets\mixins.py", line 59, in get_cached_kernel
new_kernel_handler = KernelHandler.new_from_spec(kernel_spec)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\seembha\.pixi\envs\global\Lib\site‑packages\spyder\plugins\ipythonconsole\utils\kernel_handler.py", line 393, in new_from_spec
kernel_manager.start_kernel(
File "C:\Users\seembha\.pixi\envs\global\Lib\site‑packages\jupyter_core\utils\__init__.py", line 165, in wrapped
return loop.run_until_complete(inner)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\seembha\.pixi\envs\global\Lib\asyncio\base_events.py", line 686, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "C:\Users\seembha\.pixi\envs\global\Lib\site‑packages\jupyter_client\manager.py", line 96, in wrapper
raise e
File "C:\Users\seembha\.pixi\envs\global\Lib\site‑packages\jupyter_client\manager.py", line 87, in wrapper
out = await method(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\seembha\.pixi\envs\global\Lib\site‑packages\jupyter_client\manager.py", line 435, in _async_start_kernel
kernel_cmd, kw = await self._async_pre_start_kernel(**kw)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\seembha\.pixi\envs\global\Lib\site‑packages\jupyter_client\manager.py", line 400, in _async_pre_start_kernel
kw = await self.provisioner.pre_launch(**kw)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\seembha\.pixi\envs\global\Lib\site‑packages\jupyter_client\provisioning\local_provisioner.py", line 198, in pre_launch
kernel_cmd = km.format_kernel_cmd(
^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\seembha\.pixi\envs\global\Lib\site‑packages\jupyter_client\manager.py", line 307, in format_kernel_cmd
cmd = self.kernel_spec.argv + extra_arguments
^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\seembha\.pixi\envs\global\Lib\site‑packages\spyder\plugins\ipythonconsole\utils\kernelspec.py", line 136, in argv
if conda_exe.endswith('micromamba'):
^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'endswith'
  • Enhancement request

I am using JupyterLab with the same setup and, with the help of pixi-kernel (https://github.com/renan-r-santos/pixi-kernel), it is able to select the correct python interpreter in the local environment.
I was hoping Spyder could provide similar support for pixi environments.

@ccordoba12
Copy link
Member

Hey @seembha, thanks for reporting. What's really missing in Spyder is using Pixi to activate kernels in our IPython console. We'll try to do that in 6.0.5, to be released in a couple of months. In the meantime, I'm afraid there's nothing to do about it, sorry.

@wolfv, what's the equivalent of conda run --prefix <env-name> --no-capture-output in Pixi?

@ccordoba12 ccordoba12 changed the title Add support for a single global Spyder installation to be used with multiple local pixi environments Add support to activate environments with Pixi Jan 27, 2025
@ccordoba12 ccordoba12 added this to the v6.0.5 milestone Jan 27, 2025
@Hofer-Julian
Copy link

Hofer-Julian commented Mar 3, 2025

Hey @ccordoba12 The equivalent to

  • conda run --prefix <env-name> --no-capture-output is
  • pixi run --manifest-path </path/to/manifest>

pixi run doesn't capture stdout/stderr so no flag is needed for that.

@ccordoba12
Copy link
Member

Thanks for chiming in @Hofer-Julian!

And how can we get the path to the manifest file from a Python executable one in an env created by Pixi? Or in other words, what's is the folder structure of Pixi envs created for a project?

@Hofer-Julian
Copy link

And how can we get the path to the manifest file from a Python executable one in an env created by Pixi? Or in other words, what's is the folder structure of Pixi envs created for a project?

I don't fully understand the first question but the folder structure is like this:

.pixi
├── envs
│   ├── default
│   ├── docs
│   └── lint
└── .gitignore

"default" is always there, but users can create additional environments. The folder structure of the environment itself is the same as with conda, mamba, ...

@ccordoba12
Copy link
Member

I don't fully understand the first question

Sorry, I just wanted to know how many parents up from the env's Python executable (which is the path we know in Spyder) the manifest file is located.

The folder structure of the environment itself is the same as with conda, mamba, ...

Thanks! That's also another valuable piece of info.

@ccordoba12
Copy link
Member

@dalthviz, please take a look at this one after finishing #23869.

@dalthviz
Copy link
Member

dalthviz commented Mar 7, 2025

Checking this I think some changes to spyder-kernels would be required:

And inside Spyder, as a minimum:

  • Add logic to find pixi executable. So something like find_conda but for pixi (
    def find_conda(pyexec=None):
    """
    Find conda executable.
    `pyexec` is a python executable, the relative location from which to
    attempt to locate a conda executable.
    """
    conda = None
    # First try Spyder's conda executable
    if is_conda_based_app():
    root = osp.dirname(os.environ['CONDA_EXE'])
    conda = osp.join(root, 'mamba.exe' if WINDOWS else 'mamba')
    # Next try the environment variables
    if conda is None:
    conda = os.environ.get('CONDA_EXE') or os.environ.get('MAMBA_EXE')
    # Next try searching for the executable
    if conda is None:
    conda_exec = 'conda.bat' if WINDOWS else 'conda'
    extra_paths = [
    osp.join(get_conda_root_prefix(_pyexec), 'condabin')
    for _pyexec in [sys.executable, pyexec]
    ]
    conda = find_program(conda_exec, extra_paths)
    return conda
    )
  • Add handling in the IPython statusbar for envs created with pixi (
    def update_status(self, env_info: dict):
    """Update env info."""
    if (
    # There's no need to emit this signal for remote consoles because
    # other plugins can only react to local interpreter changes.
    not self.current_shellwidget.is_remote()
    and env_info != self._current_env_info
    ):
    new_interpreter = env_info["path"]
    logger.debug(f"Console interpreter changed to {new_interpreter}")
    self.sig_interpreter_changed.emit(new_interpreter)
    self._current_env_info = env_info
    if env_info["env_type"] == PythonEnvType.Conda:
    env_type = "Conda"
    elif env_info["env_type"] == PythonEnvType.PyEnv:
    env_type = "Pyenv"
    else:
    env_type = _("Custom")
    # The format to display is:
    # env_type: env_name (Python python_version)
    text = (
    env_type
    + ": "
    + env_info["name"]
    + " (Python "
    + env_info["python_version"]
    + ")"
    )
    self.set_value(text)
    )
  • Add logic to use pixi run for the kernel command (
    # Command used to start kernels
    kernel_cmd = []
    if is_conda_env(pyexec=pyexec):
    # If executable is a conda environment, use "run" subcommand to
    # activate it and run spyder-kernels.
    conda_exe = find_conda()
    if not conda_exe:
    # Raise error since we were unable to determine the path to
    # the conda executable (e.g when Anaconda/Miniconda was
    # installed in a non-standard location).
    # See spyder-ide/spyder#23595
    raise SpyderKernelError(
    _(
    "Spyder couldn't find conda or mamba in your system "
    "to activate the kernel's environment. Please add the "
    "directory where the conda or mamba executable is "
    "located to your PATH environment variable for it to "
    "be detected."
    )
    )
    conda_exe_version = conda_version(conda_executable=conda_exe)
    kernel_cmd.extend([
    conda_exe,
    'run',
    '--prefix',
    get_conda_env_path(pyexec)
    ])
    # We need to use this flag to prevent conda_exe from capturing the
    # kernel process stdout/stderr streams. That way we are able to
    # show them in Spyder.
    if conda_exe.endswith(('micromamba', 'micromamba.exe')):
    kernel_cmd.extend(['--attach', '""'])
    elif conda_exe_version >= parse("4.9"):
    # Note: We use --no-capture-output instead of --live-stream
    # here because it works for older Conda versions (conda>=4.9).
    kernel_cmd.append('--no-capture-output')
    else:
    # Raise error since an unsupported conda version is being used
    # (conda<4.9).
    # See spyder-ide/spyder#22554
    raise SpyderKernelError(
    _(
    "The detected version of Conda is too old and not "
    "supported by Spyder. The minimum supported version is "
    "4.9 and currently you have {conda_version}.<br><br>."
    "<b>Note</b>: You need to restart Spyder after "
    "updating Conda for the change to take effect."
    ).format(conda_version=conda_exe_version)
    )
    kernel_cmd.extend([
    pyexec,
    # This is necessary to avoid a spurious message on Windows.
    # Fixes spyder-ide/spyder#20800.
    '-Xfrozen_modules=off',
    '-m', 'spyder_kernels.console',
    '-f', '{connection_file}'
    ])
    )
    • I think the initial part of the command to start kernels with pixi then would look something like:

      <pixi_executable> run --environment <env name> --manifest-path <path_to_manifest>
      

Other things that could be related with pixi support are detecting/listing pixi created envs and add from the IPython console menu the possibility to launch a console using a pixi created env Python interpreter but maybe that is out of the scope of this issue 🤔

Anyhow, after these changes are done I think we will need to enforce an update of spyder-kernels right @ccordoba12 ? If that is the case, maybe this issue should be part of the 6.1.0 milestone ? Or you were thinking on a different implementation path to fix this issue?

As a side note, trying to understand why the initial traceback appeared, I did some testing with pixi and interestingly the command to run the kernel worked and the console launched without problems for me whe choosing a Python interpreter from a pixi created env. In fact if I do conda activate --prefix <path to prefix created env> I'm able to activate the env. Following this, my guess is that since envs created with pixi use conda packages, Spyder detects envs created with pixi as conda envs. This detection then causes an error if you don't have conda installed. Thinking about that, with the changes over #23869 you would now see a message indicating that conda was not found and that you need to add it to your path which maybe could be confusing?🤔

@ccordoba12
Copy link
Member

Anyhow, after these changes are done I think we will need to enforce an update of spyder-kernels right @ccordoba12 ? If that is the case, maybe this issue should be part of the 6.1.0 milestone ? Or you were thinking on a different implementation path to fix this issue?

I thought only step 3 of the ones you listed above was necessary for this. But if changes are necessary to get_pythonenv_info, then you're right, this needs to wait for 6.1.0

Thinking about that, with the changes over #23869 you would now see a message indicating that conda was not found and that you need to add it to your path which maybe could be confusing?🤔

Yeah, a little bit. We could improve that by detecting if .pixi is in the interpreter path and showing a different message in that case. Something like: Spyder doesn't support Pixi environments at the time, but it will in version 6.1.0.

Would you like to submit a PR for that?

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

Successfully merging a pull request may close this issue.

4 participants