Skip to content

Conversation

@lipefree
Copy link
Contributor

@lipefree lipefree commented May 23, 2025

Summary

Implement what was discussed in astral-sh/ty#265. Pixi and Conda set the CONDA_PREFIX environment variable and not VIRTUAL_ENV. With this PR, if no --python flag has been passed and VIRTUAL_ENV has not been set then we check CONDA_PREFIX and resolve the path.

I am still not sure about how to implement: CONDA_PREFIX is checked only if there's no .venv directory in the project root.

My guess is it should be something like this but the .unwrap_or_else() is still confusing to me and I can't figure out how to check for CONDA_PREFIX after the .unwrap_or_else() .

SearchPathSettings {
            extra_paths: extra_paths
                .unwrap_or_default()
                .into_iter()
                .map(|path| path.absolute(project_root, system))
                .collect(),
            src_roots,
            custom_typeshed: typeshed.map(|path| path.absolute(project_root, system)),
            python_path: python
                .map(|python_path| {
                    PythonPath::from_cli_flag(python_path.absolute(project_root, system))
                })
                .or_else(|| {
                    std::env::var("VIRTUAL_ENV")
                        .ok()
                        .map(PythonPath::from_virtual_env_var)
                })
                .unwrap_or_else(|| PythonPath::Discover(project_root.to_path_buf())
                .or_else(|| {
                                    std::env::var("CONDA_PREFIX")
                                        .ok()
                                        .map(PythonPath::from_conda_prefix_var)
                                })),
        }

Closes astral-sh/ty#265

Test Plan

I reproduced the error from #265, and now there is no more lint:unresolved-import error. I am still not familar with the markdown tests but I will do more commits to provide tests.

This is my first opensource contribution and I am still a student with 0 experience in Rust, please give me constructive feedbacks! I will try to learn as quickly as I can!

@AlexWaygood AlexWaygood added the ty Multi-file analysis & type inference label May 23, 2025
@github-actions
Copy link
Contributor

github-actions bot commented May 23, 2025

mypy_primer results

No ecosystem changes detected ✅

@lipefree lipefree changed the title [Ty] Resolving Python path using CONDA_PREFIX variable to support Conda and Pixi [ty] Resolving Python path using CONDA_PREFIX variable to support Conda and Pixi May 23, 2025
@sharkdp sharkdp removed their request for review May 23, 2025 07:28
.map(PythonPath::from_virtual_env_var)
})
.unwrap_or_else(|| PythonPath::Discover(project_root.to_path_buf())),
.or_else(|| Some(PythonPath::Discover(project_root.to_path_buf())))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If someone can help me out since I am new to Rust, this is the solution I came up with to deal with the compiler errors. Is it fine to wrap PythonPath::Discover(project_root.to_path_buf()) in Some(.) and is it fine to simply unwrap std::env::var("CONDA_PREFIX").ok().map(PythonPath::from_conda_prefix_var) ?

Copy link
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

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

Thanks for working on this. I left an inline comment. I hope the rest of the team agrees with my proposed change but that should simplify your life

.map(PythonPath::from_virtual_env_var)
})
.unwrap_or_else(|| PythonPath::Discover(project_root.to_path_buf())),
.or_else(|| Some(PythonPath::Discover(project_root.to_path_buf())))
Copy link
Member

Choose a reason for hiding this comment

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

This may not work as intended (unless I'm misreading the code here).

The .unwrap_or_else will only look at the CONDA_PREFIX if the or_else on line 177 returned None. However, this can never be the case, because the or_else on line 177 always.

Given that CONDA_PREFIX is more explicit than an existing .venv directory, I think I'm leaning towards changing the order to

  • VIRTUAL_ENV if it is set
  • CONDA_PREFIX
  • Fallback to inspecting the project directory structure

That should also simplify your live because all you need to do is add an or_else similar to the VIRTUAL_ENV branch before the unwrap_or_else

Copy link
Member

Choose a reason for hiding this comment

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

Given that CONDA_PREFIX is more explicit than an existing .venv directory

What worries me here is that it's possible (though not necessarily recommended) to create a virtual environment inside a conda environment. If the user has done that, it might be pretty surprising if we require them to explicitly activate the virtual environment in order for us to recognise it (it goes against the general "Astral philosophy" of not requiring users to imperatively activate their virtual environments).

I say "activate a virtual environment" because I think almost no one sets the VIRTUAL_ENV environment variable manually: it's usually set as part of the virtual-environment activation (source .venv/bin/activate in the user's shell)

Copy link
Member

Choose a reason for hiding this comment

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

Fair enough. Implementing this with this specific discovery ordering would be much more complicated because it can't be implemented here. Instead, it would need to happen inside the search path setting resolution

Copy link
Member

Choose a reason for hiding this comment

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

(it goes against the general "Astral philosophy" of not requiring users to imperatively activate their virtual environments).

Fair, but it would be activate if we have a tigher uv integration

Copy link
Member

Choose a reason for hiding this comment

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

I doubt people who use conda environments are going to want to run ty via uv — pixi or conda are the project managers you probably want to use if you're using conda environments

Copy link
Member

@MichaReiser MichaReiser May 23, 2025

Choose a reason for hiding this comment

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

Now I'm confused. if you use conda, then changing the order wouldn't matter? And I think it's fine to ask for explicit configuration if you have a project where you have an activated CONDA env and a nested venv. It's just the best information that the user has given us and a environment variable is, IMO, always more explicit than using some heuristic.

Copy link
Member

@AlexWaygood AlexWaygood May 23, 2025

Choose a reason for hiding this comment

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

OK, yes, that's fair enough. If the user is using a non-conda virtual environment inside their conda environment, I guess in 2025 they might be using a non-conda project manager to run commands in their project and relying on that non-conda project manager to keep that virtual environment activated for the duration of those commands. I guess I'm wrapping my head round this paradigm too — I haven't used conda since before uv was a thing 😆

In that case, I'm OK with what you suggest: preferring an explicitly activated conda environment over a virtual environment unless the virtual environment has also been explicitly activated (either by the user or by a non-conda project manager such as uv/hatch/poetry/pdm).

We should make sure we write up the rationale for this in a comment, though!

Copy link
Member

Choose a reason for hiding this comment

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

I'm also very open to changing this if it proves problematic. The benefit of still being in preview :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"The .unwrap_or_else will only look at the CONDA_PREFIX if the or_else on line 177 returned None. However, this can never be the case, because the or_else on line 177 always." so from what I understand, you are saying that even if no .venv is found, we will never reach line unwrap_or_else in line 178. From my testing, if no .venv directory is found then ty will set the python path using CONDA_PREFIX which means that unwrap_or_else in line 178 is used (It's my first time writing Rust so please correct me).

Also from what I understand from your conversations is that I should revert the changes of my last commit and have the following check order :

  • VIRTUAL_ENV if it is set
  • CONDA_PREFIX if it is set
  • Fallback to inspecting the project directory structure

Thank you for reviewing my PR!

@MichaReiser MichaReiser self-requested a review May 23, 2025 14:01
@lipefree
Copy link
Contributor Author

I was looking for a way to write tests by setting/unsetting the VIRTUAL_ENV and CONDA_PREFIX environment variables and having a .venv directory. But looking at (writing tests guide)[https://github.com/astral-sh/ruff/tree/main/crates/ty_test], I was not able to understand if the current TOML-based configuration format support my testing case.

@MichaReiser
Copy link
Member

our markdown tests don't support setting environment variables. The best you can try is to write a CLI test in

fn test_run_in_sub_directory() -> anyhow::Result<()> {

@MichaReiser
Copy link
Member

I'll convert this to draft. @lipefree you can convert it back to "ready for review" once your PR is ready for another round. This makes it easier for me to know when I should take a look at your PR

@MichaReiser MichaReiser marked this pull request as draft May 23, 2025 15:24
@lipefree
Copy link
Contributor Author

our markdown tests don't support setting environment variables. The best you can try is to write a CLI test in

fn test_run_in_sub_directory() -> anyhow::Result<()> {

I tried my best for the CLI test. I don't know if it is enough for you.

@lipefree lipefree marked this pull request as ready for review May 23, 2025 17:00
Copy link
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

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

This is great and nice write up in the test!

@MichaReiser
Copy link
Member

Oh no, the tests don't work on Windows because the venv path is slightly different. Do you want to take a look?

@lipefree
Copy link
Contributor Author

Oh no, the tests don't work on Windows because the venv path is slightly different. Do you want to take a look?

I don't have a Windows machine so it is hard for me to look at how it works. I guess I just need to figure out how Conda environments are structured on Windows but then I don't know how to have OS dependent tests run on my mac (apart from letting the CI do it for me which is quite slow).

@MichaReiser
Copy link
Member

I also don't have a windows machine ;(

Here's an example on how you can have custom logic based on whether you're on windows.

ruff/crates/ty/tests/cli.rs

Lines 957 to 961 in d098118

let config_env_var = if cfg!(windows) {
"APPDATA"
} else {
"XDG_CONFIG_HOME"
};

"#,
),
(
"conda-env/lib/python3.13/site-packages/package1/__init__.py",
Copy link
Member

@AlexWaygood AlexWaygood May 23, 2025

Choose a reason for hiding this comment

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

on Windows the path needs to be conda-env/Lib/site-packages/package1/__init__.py rather than conda-env/lib/python3.13/site-packages/package1/__init__.py

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does "conda-env/Lib/site-packages/package1/init.py" works instead of "conda-env/Lib/site-packages/package1/init.py" ? I am just trusting my copy/paste skill at this point since I can't test locally.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok it's just markdown doing some formatting. I got it.

Copy link
Member

Choose a reason for hiding this comment

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

I mistyped my message originally -- sorry! If you refresh your browser, the edited version of my message should be displayed :-)

It does indeed need to be __init__.py rather than init.py

Copy link
Member

Choose a reason for hiding this comment

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

Ok it's just markdown doing some formatting. I got it.

yeah, exactly 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I hope it will pass now and that I did not do any typo !

@MichaReiser
Copy link
Member

Perfect! ty

@MichaReiser MichaReiser merged commit 53d19f8 into astral-sh:main May 23, 2025
35 checks passed
@AlexWaygood
Copy link
Member

@lipefree congrats on your first open-source contribution!! This is a fantastic feature for us to have 😃

@lipefree
Copy link
Contributor Author

Thank you for your help @MichaReiser @AlexWaygood on my first contribution !

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

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support resolving modules from conda or pixi environments

3 participants