@@ -145,6 +145,38 @@ def _version_nodot(version):
145
145
print(json.dumps(sysconfig.get_paths()))
146
146
"""
147
147
148
+ GET_PATHS_FOR_GENERIC_ENVS = """\
149
+ # We can't use sysconfig.get_paths() because
150
+ # on some distributions it does not return the proper paths
151
+ # (those used by pip for instance). We go through distutils
152
+ # to get the proper ones.
153
+ import json
154
+ import site
155
+ import sysconfig
156
+
157
+ from distutils.command.install import SCHEME_KEYS # noqa
158
+ from distutils.core import Distribution
159
+
160
+ d = Distribution()
161
+ d.parse_config_files()
162
+ obj = d.get_command_obj("install", create=True)
163
+ obj.finalize_options()
164
+
165
+ paths = sysconfig.get_paths().copy()
166
+ for key in SCHEME_KEYS:
167
+ if key == "headers":
168
+ # headers is not a path returned by sysconfig.get_paths()
169
+ continue
170
+
171
+ paths[key] = getattr(obj, f"install_{key}")
172
+
173
+ if site.check_enableusersite() and hasattr(obj, "install_usersite"):
174
+ paths["usersite"] = getattr(obj, "install_usersite")
175
+ paths["userbase"] = getattr(obj, "install_userbase")
176
+
177
+ print(json.dumps(paths))
178
+ """
179
+
148
180
149
181
class SitePackages :
150
182
def __init__ (
@@ -615,7 +647,7 @@ def remove(self, python): # type: (str) -> Env
615
647
616
648
self .remove_venv (venv )
617
649
618
- return VirtualEnv (venv )
650
+ return VirtualEnv (venv , venv )
619
651
620
652
def create_venv (
621
653
self , io , name = None , executable = None , force = False
@@ -848,15 +880,21 @@ def get_system_env(
848
880
(e.g. plugin installation or self update).
849
881
"""
850
882
prefix , base_prefix = Path (sys .prefix ), Path (cls .get_base_prefix ())
883
+ env = SystemEnv (prefix )
851
884
if not naive :
852
- try :
853
- Path (__file__ ).relative_to (prefix )
854
- except ValueError :
855
- pass
885
+ if prefix .joinpath ("poetry_env" ).exists ():
886
+ env = GenericEnv (base_prefix , child_env = env )
856
887
else :
857
- return GenericEnv ( base_prefix )
888
+ from poetry . locations import data_dir
858
889
859
- return SystemEnv (prefix )
890
+ try :
891
+ prefix .relative_to (data_dir ())
892
+ except ValueError :
893
+ pass
894
+ else :
895
+ env = GenericEnv (base_prefix , child_env = env )
896
+
897
+ return env
860
898
861
899
@classmethod
862
900
def get_base_prefix (cls ): # type: () -> str
@@ -892,6 +930,11 @@ def __init__(self, path, base=None): # type: (Path, Optional[Path]) -> None
892
930
893
931
self ._base = base or path
894
932
933
+ self ._executable = "python"
934
+ self ._pip_executable = "pip"
935
+
936
+ self .find_executables ()
937
+
895
938
self ._marker_env = None
896
939
self ._pip_version = None
897
940
self ._site_packages = None
@@ -922,7 +965,7 @@ def python(self): # type: () -> str
922
965
"""
923
966
Path to current python executable
924
967
"""
925
- return self ._bin ("python" )
968
+ return self ._bin (self . _executable )
926
969
927
970
@property
928
971
def marker_env (self ):
@@ -931,12 +974,16 @@ def marker_env(self):
931
974
932
975
return self ._marker_env
933
976
977
+ @property
978
+ def parent_env (self ): # type: () -> GenericEnv
979
+ return GenericEnv (self .base , child_env = self )
980
+
934
981
@property
935
982
def pip (self ): # type: () -> str
936
983
"""
937
984
Path to current pip executable
938
985
"""
939
- return self ._bin ("pip" )
986
+ return self ._bin (self . _pip_executable )
940
987
941
988
@property
942
989
def platform (self ): # type: () -> str
@@ -1028,6 +1075,35 @@ def get_base_prefix(cls): # type: () -> str
1028
1075
1029
1076
return sys .prefix
1030
1077
1078
+ def find_executables (self ): # type: () -> None
1079
+ python_executables = sorted (
1080
+ [
1081
+ p .name
1082
+ for p in self ._bin_dir .glob ("python*" )
1083
+ if re .match (r"python(?:\d+(?:\.\d+)?)?(?:\.exe)?$" , p .name )
1084
+ ]
1085
+ )
1086
+ if python_executables :
1087
+ executable = python_executables [0 ]
1088
+ if executable .endswith (".exe" ):
1089
+ executable = executable [:- 4 ]
1090
+
1091
+ self ._executable = executable
1092
+
1093
+ pip_executables = sorted (
1094
+ [
1095
+ p .name
1096
+ for p in self ._bin_dir .glob ("pip*" )
1097
+ if re .match (r"pip(?:\d+(?:\.\d+)?)?(?:\.exe)?$" , p .name )
1098
+ ]
1099
+ )
1100
+ if pip_executables :
1101
+ pip_executable = pip_executables [0 ]
1102
+ if pip_executable .endswith (".exe" ):
1103
+ pip_executable = pip_executable [:- 4 ]
1104
+
1105
+ self ._pip_executable = pip_executable
1106
+
1031
1107
def get_version_info (self ): # type: () -> Tuple[int]
1032
1108
raise NotImplementedError ()
1033
1109
@@ -1063,6 +1139,9 @@ def run(self, bin, *args, **kwargs):
1063
1139
cmd = [bin ] + list (args )
1064
1140
return self ._run (cmd , ** kwargs )
1065
1141
1142
+ def run_python (self , * args , ** kwargs ):
1143
+ return self .run (self ._executable , * args , ** kwargs )
1144
+
1066
1145
def run_pip (self , * args , ** kwargs ):
1067
1146
pip = self .get_pip_command ()
1068
1147
cmd = pip + list (args )
@@ -1133,7 +1212,11 @@ def _bin(self, bin): # type: (str) -> str
1133
1212
"""
1134
1213
Return path to the given executable.
1135
1214
"""
1136
- bin_path = (self ._bin_dir / bin ).with_suffix (".exe" if self ._is_windows else "" )
1215
+ if self ._is_windows and not bin .endswith (".exe" ):
1216
+ bin_path = self ._bin_dir / (bin + ".exe" )
1217
+ else :
1218
+ bin_path = self ._bin_dir / bin
1219
+
1137
1220
if not bin_path .exists ():
1138
1221
# On Windows, some executables can be in the base path
1139
1222
# This is especially true when installing Python with
@@ -1144,7 +1227,11 @@ def _bin(self, bin): # type: (str) -> str
1144
1227
# that creates a fake virtual environment pointing to
1145
1228
# a base Python install.
1146
1229
if self ._is_windows :
1147
- bin_path = (self ._path / bin ).with_suffix (".exe" )
1230
+ if not bin .endswith (".exe" ):
1231
+ bin_path = self ._bin_dir / (bin + ".exe" )
1232
+ else :
1233
+ bin_path = self ._path / bin
1234
+
1148
1235
if bin_path .exists ():
1149
1236
return str (bin_path )
1150
1237
@@ -1270,16 +1357,18 @@ def __init__(self, path, base=None): # type: (Path, Optional[Path]) -> None
1270
1357
# In this case we need to get sys.base_prefix
1271
1358
# from inside the virtualenv.
1272
1359
if base is None :
1273
- self ._base = Path (self .run ("python" , "-" , input_ = GET_BASE_PREFIX ).strip ())
1360
+ self ._base = Path (
1361
+ self .run (self ._executable , "-" , input_ = GET_BASE_PREFIX ).strip ()
1362
+ )
1274
1363
1275
1364
@property
1276
1365
def sys_path (self ): # type: () -> List[str]
1277
- output = self .run ("python" , "-" , input_ = GET_SYS_PATH )
1366
+ output = self .run (self . _executable , "-" , input_ = GET_SYS_PATH )
1278
1367
1279
1368
return json .loads (output )
1280
1369
1281
1370
def get_version_info (self ): # type: () -> Tuple[int]
1282
- output = self .run ("python" , "-" , input_ = GET_PYTHON_VERSION )
1371
+ output = self .run (self . _executable , "-" , input_ = GET_PYTHON_VERSION )
1283
1372
1284
1373
return tuple ([int (s ) for s in output .strip ().split ("." )])
1285
1374
@@ -1289,7 +1378,7 @@ def get_python_implementation(self): # type: () -> str
1289
1378
def get_pip_command (self ): # type: () -> List[str]
1290
1379
# We're in a virtualenv that is known to be sane,
1291
1380
# so assume that we have a functional pip
1292
- return [self ._bin ("pip" )]
1381
+ return [self ._bin (self . _pip_executable )]
1293
1382
1294
1383
def get_supported_tags (self ): # type: () -> List[Tag]
1295
1384
file_path = Path (packaging .tags .__file__ )
@@ -1317,12 +1406,12 @@ def get_supported_tags(self): # type: () -> List[Tag]
1317
1406
"""
1318
1407
)
1319
1408
1320
- output = self .run ("python" , "-" , input_ = script )
1409
+ output = self .run (self . _executable , "-" , input_ = script )
1321
1410
1322
1411
return [Tag (* t ) for t in json .loads (output )]
1323
1412
1324
1413
def get_marker_env (self ): # type: () -> Dict[str, Any]
1325
- output = self .run ("python" , "-" , input_ = GET_ENVIRONMENT_INFO )
1414
+ output = self .run (self . _executable , "-" , input_ = GET_ENVIRONMENT_INFO )
1326
1415
1327
1416
return json .loads (output )
1328
1417
@@ -1335,7 +1424,7 @@ def get_pip_version(self): # type: () -> Version
1335
1424
return Version .parse (m .group (1 ))
1336
1425
1337
1426
def get_paths (self ): # type: () -> Dict[str, str]
1338
- output = self .run ("python" , "-" , input_ = GET_PATHS )
1427
+ output = self .run (self . _executable , "-" , input_ = GET_PATHS )
1339
1428
1340
1429
return json .loads (output )
1341
1430
@@ -1388,6 +1477,81 @@ def _updated_path(self):
1388
1477
1389
1478
1390
1479
class GenericEnv (VirtualEnv ):
1480
+ def __init__ (
1481
+ self , path , base = None , child_env = None
1482
+ ): # type: (Path, Optional[Path], Optional[Env]) -> None
1483
+ self ._child_env = child_env
1484
+
1485
+ super (GenericEnv , self ).__init__ (path , base = base )
1486
+
1487
+ def find_executables (self ): # type: () -> None
1488
+ patterns = [("python*" , "pip*" )]
1489
+
1490
+ if self ._child_env :
1491
+ minor_version = "{}.{}" .format (
1492
+ self ._child_env .version_info [0 ], self ._child_env .version_info [1 ]
1493
+ )
1494
+ major_version = "{}" .format (self ._child_env .version_info [0 ])
1495
+ patterns = [
1496
+ ("python{}" .format (minor_version ), "pip{}" .format (minor_version )),
1497
+ ("python{}" .format (major_version ), "pip{}" .format (major_version )),
1498
+ ]
1499
+
1500
+ python_executable = None
1501
+ pip_executable = None
1502
+
1503
+ for python_pattern , pip_pattern in patterns :
1504
+ if python_executable and pip_executable :
1505
+ break
1506
+
1507
+ if not python_executable :
1508
+ python_executables = sorted (
1509
+ [
1510
+ p .name
1511
+ for p in self ._bin_dir .glob (python_pattern )
1512
+ if re .match (r"python(?:\d+(?:\.\d+)?)?(?:\.exe)?$" , p .name )
1513
+ ]
1514
+ )
1515
+
1516
+ if python_executables :
1517
+ executable = python_executables [0 ]
1518
+ if executable .endswith (".exe" ):
1519
+ executable = executable [:- 4 ]
1520
+
1521
+ python_executable = executable
1522
+
1523
+ if not pip_executable :
1524
+ pip_executables = sorted (
1525
+ [
1526
+ p .name
1527
+ for p in self ._bin_dir .glob (pip_pattern )
1528
+ if re .match (r"pip(?:\d+(?:\.\d+)?)?(?:\.exe)?$" , p .name )
1529
+ ]
1530
+ )
1531
+ if pip_executables :
1532
+ pip_executable = pip_executables [0 ]
1533
+ if pip_executable .endswith (".exe" ):
1534
+ pip_executable = pip_executable [:- 4 ]
1535
+
1536
+ pip_executable = pip_executable
1537
+
1538
+ if python_executable :
1539
+ self ._executable = python_executable
1540
+
1541
+ if pip_executable :
1542
+ self ._pip_executable = pip_executable
1543
+
1544
+ def get_paths (self ): # type: () -> Dict[str, str]
1545
+ output = self .run (self ._executable , "-" , input_ = GET_PATHS_FOR_GENERIC_ENVS )
1546
+
1547
+ return json .loads (output )
1548
+
1549
+ def execute (self , bin , * args , ** kwargs ): # type: (str, str, Any) -> Optional[int]
1550
+ return super (VirtualEnv , self ).execute (bin , * args , ** kwargs )
1551
+
1552
+ def _run (self , cmd , ** kwargs ): # type: (List[str], Any) -> Optional[int]
1553
+ return super (VirtualEnv , self )._run (cmd , ** kwargs )
1554
+
1391
1555
def is_venv (self ): # type: () -> bool
1392
1556
return self ._path != self ._base
1393
1557
0 commit comments