@@ -26,6 +26,26 @@ pub fn is_venv(env: &PythonEnv) -> bool {
2626pub fn is_venv_dir ( path : & Path ) -> bool {
2727 PyVenvCfg :: find ( path) . is_some ( )
2828}
29+
30+ pub fn is_venv_uv ( env : & PythonEnv ) -> bool {
31+ if let Some ( cfg) = PyVenvCfg :: find ( env. executable . parent ( ) . unwrap_or ( Path :: new ( "" ) ) ) {
32+ return cfg. is_uv ( ) ;
33+ }
34+ if let Some ( ref prefix) = env. prefix {
35+ if let Some ( cfg) = PyVenvCfg :: find ( prefix) {
36+ return cfg. is_uv ( ) ;
37+ }
38+ }
39+ false
40+ }
41+
42+ pub fn is_venv_uv_dir ( path : & Path ) -> bool {
43+ if let Some ( cfg) = PyVenvCfg :: find ( path) {
44+ cfg. is_uv ( )
45+ } else {
46+ false
47+ }
48+ }
2949pub struct Venv { }
3050
3151impl Venv {
@@ -43,7 +63,7 @@ impl Locator for Venv {
4363 LocatorKind :: Venv
4464 }
4565 fn supported_categories ( & self ) -> Vec < PythonEnvironmentKind > {
46- vec ! [ PythonEnvironmentKind :: Venv ]
66+ vec ! [ PythonEnvironmentKind :: Venv , PythonEnvironmentKind :: VenvUv ]
4767 }
4868
4969 fn try_from ( & self , env : & PythonEnv ) -> Option < PythonEnvironment > {
@@ -67,10 +87,17 @@ impl Locator for Venv {
6787 // Get the name from the prefix if it exists.
6888 let cfg = PyVenvCfg :: find ( env. executable . parent ( ) ?)
6989 . or_else ( || PyVenvCfg :: find ( & env. prefix . clone ( ) ?) ) ;
70- let name = cfg. and_then ( |cfg| cfg. prompt ) ;
90+ let name = cfg. as_ref ( ) . and_then ( |cfg| cfg. prompt . clone ( ) ) ;
91+
92+ // Determine the environment kind based on whether it was created with UV
93+ let kind = if cfg. as_ref ( ) . map_or ( false , |c| c. is_uv ( ) ) {
94+ PythonEnvironmentKind :: VenvUv
95+ } else {
96+ PythonEnvironmentKind :: Venv
97+ } ;
7198
7299 Some (
73- PythonEnvironmentBuilder :: new ( Some ( PythonEnvironmentKind :: Venv ) )
100+ PythonEnvironmentBuilder :: new ( Some ( kind ) )
74101 . name ( name)
75102 . executable ( Some ( env. executable . clone ( ) ) )
76103 . version ( version)
@@ -88,3 +115,107 @@ impl Locator for Venv {
88115 // We expect the user of this class to call `is_compatible`
89116 }
90117}
118+
119+ #[ cfg( test) ]
120+ mod tests {
121+ use super :: * ;
122+ use std:: path:: PathBuf ;
123+
124+ #[ test]
125+ fn test_is_venv_uv_dir_detects_uv_environment ( ) {
126+ use std:: fs;
127+ let test_dir = PathBuf :: from ( "/tmp/test_uv_env_venv" ) ;
128+ fs:: create_dir_all ( & test_dir) . unwrap ( ) ;
129+ let pyvenv_cfg = test_dir. join ( "pyvenv.cfg" ) ;
130+ let contents = "home = /usr/bin/python3.12\n implementation = CPython\n uv = 0.8.14\n version_info = 3.12.11\n include-system-site-packages = false\n prompt = test-uv-env\n " ;
131+ fs:: write ( & pyvenv_cfg, contents) . unwrap ( ) ;
132+
133+ assert ! ( is_venv_uv_dir( & test_dir) , "Should detect UV environment" ) ;
134+
135+ fs:: remove_dir_all ( & test_dir) . ok ( ) ;
136+ }
137+
138+ #[ test]
139+ fn test_is_venv_uv_dir_does_not_detect_regular_environment ( ) {
140+ use std:: fs;
141+ let test_dir = PathBuf :: from ( "/tmp/test_regular_env_venv" ) ;
142+ fs:: create_dir_all ( & test_dir) . unwrap ( ) ;
143+ let pyvenv_cfg = test_dir. join ( "pyvenv.cfg" ) ;
144+ let contents = "home = /usr/bin/python3.12\n include-system-site-packages = false\n version = 3.13.5\n executable = /usr/bin/python3.12\n command = python -m venv /path/to/env\n " ;
145+ fs:: write ( & pyvenv_cfg, contents) . unwrap ( ) ;
146+
147+ assert ! (
148+ !is_venv_uv_dir( & test_dir) ,
149+ "Should not detect regular venv as UV environment"
150+ ) ;
151+
152+ fs:: remove_dir_all ( & test_dir) . ok ( ) ;
153+ }
154+
155+ #[ test]
156+ fn test_is_venv_uv_dir_handles_nonexistent_environment ( ) {
157+ let nonexistent_path = PathBuf :: from ( "/tmp/nonexistent_env" ) ;
158+ assert ! (
159+ !is_venv_uv_dir( & nonexistent_path) ,
160+ "Should not detect non-existent environment as UV"
161+ ) ;
162+ }
163+
164+ #[ test]
165+ fn test_venv_locator_detects_uv_kind ( ) {
166+ use pet_core:: env:: PythonEnv ;
167+ use std:: fs;
168+
169+ // Create a test UV environment
170+ let test_dir = PathBuf :: from ( "/tmp/test_locator_uv" ) ;
171+ let bin_dir = test_dir. join ( "bin" ) ;
172+ fs:: create_dir_all ( & bin_dir) . unwrap ( ) ;
173+
174+ let pyvenv_cfg = test_dir. join ( "pyvenv.cfg" ) ;
175+ let contents = "home = /usr/bin/python3.12\n implementation = CPython\n uv = 0.8.14\n version_info = 3.12.11\n include-system-site-packages = false\n prompt = test-uv-env\n " ;
176+ fs:: write ( & pyvenv_cfg, contents) . unwrap ( ) ;
177+
178+ let python_exe = bin_dir. join ( "python" ) ;
179+ fs:: write ( & python_exe, "" ) . unwrap ( ) ; // Create dummy python executable
180+
181+ let env = PythonEnv :: new ( python_exe. clone ( ) , Some ( test_dir. clone ( ) ) , Some ( "3.12.11" . to_string ( ) ) ) ;
182+ let locator = Venv :: new ( ) ;
183+
184+ if let Some ( result) = locator. try_from ( & env) {
185+ assert_eq ! ( result. kind, Some ( PythonEnvironmentKind :: VenvUv ) , "UV environment should be detected as VenvUv" ) ;
186+ } else {
187+ panic ! ( "Locator should detect UV environment" ) ;
188+ }
189+
190+ fs:: remove_dir_all ( & test_dir) . ok ( ) ;
191+ }
192+
193+ #[ test]
194+ fn test_venv_locator_detects_regular_venv_kind ( ) {
195+ use pet_core:: env:: PythonEnv ;
196+ use std:: fs;
197+
198+ // Create a test regular venv environment
199+ let test_dir = PathBuf :: from ( "/tmp/test_locator_regular" ) ;
200+ let bin_dir = test_dir. join ( "bin" ) ;
201+ fs:: create_dir_all ( & bin_dir) . unwrap ( ) ;
202+
203+ let pyvenv_cfg = test_dir. join ( "pyvenv.cfg" ) ;
204+ let contents = "home = /usr/bin/python3.12\n include-system-site-packages = false\n version = 3.13.5\n executable = /usr/bin/python3.12\n command = python -m venv /path/to/env\n " ;
205+ fs:: write ( & pyvenv_cfg, contents) . unwrap ( ) ;
206+
207+ let python_exe = bin_dir. join ( "python" ) ;
208+ fs:: write ( & python_exe, "" ) . unwrap ( ) ; // Create dummy python executable
209+
210+ let env = PythonEnv :: new ( python_exe. clone ( ) , Some ( test_dir. clone ( ) ) , Some ( "3.13.5" . to_string ( ) ) ) ;
211+ let locator = Venv :: new ( ) ;
212+
213+ if let Some ( result) = locator. try_from ( & env) {
214+ assert_eq ! ( result. kind, Some ( PythonEnvironmentKind :: Venv ) , "Regular venv should be detected as Venv" ) ;
215+ } else {
216+ panic ! ( "Locator should detect regular venv environment" ) ;
217+ }
218+
219+ fs:: remove_dir_all ( & test_dir) . ok ( ) ;
220+ }
221+ }
0 commit comments