Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pip/_internal/locations/_distutils.py: 29%

75 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:48 +0000

1"""Locations where we look for configs, install stuff, etc""" 

2 

3# The following comment should be removed at some point in the future. 

4# mypy: strict-optional=False 

5 

6# If pip's going to use distutils, it should not be using the copy that setuptools 

7# might have injected into the environment. This is done by removing the injected 

8# shim, if it's injected. 

9# 

10# See https://github.com/pypa/pip/issues/8761 for the original discussion and 

11# rationale for why this is done within pip. 

12try: 

13 __import__("_distutils_hack").remove_shim() 

14except (ImportError, AttributeError): 

15 pass 

16 

17import logging 

18import os 

19import sys 

20from distutils.cmd import Command as DistutilsCommand 

21from distutils.command.install import SCHEME_KEYS 

22from distutils.command.install import install as distutils_install_command 

23from distutils.sysconfig import get_python_lib 

24from typing import Dict, List, Optional, Union, cast 

25 

26from pip._internal.models.scheme import Scheme 

27from pip._internal.utils.compat import WINDOWS 

28from pip._internal.utils.virtualenv import running_under_virtualenv 

29 

30from .base import get_major_minor_version 

31 

32logger = logging.getLogger(__name__) 

33 

34 

35def distutils_scheme( 

36 dist_name: str, 

37 user: bool = False, 

38 home: Optional[str] = None, 

39 root: Optional[str] = None, 

40 isolated: bool = False, 

41 prefix: Optional[str] = None, 

42 *, 

43 ignore_config_files: bool = False, 

44) -> Dict[str, str]: 

45 """ 

46 Return a distutils install scheme 

47 """ 

48 from distutils.dist import Distribution 

49 

50 dist_args: Dict[str, Union[str, List[str]]] = {"name": dist_name} 

51 if isolated: 

52 dist_args["script_args"] = ["--no-user-cfg"] 

53 

54 d = Distribution(dist_args) 

55 if not ignore_config_files: 

56 try: 

57 d.parse_config_files() 

58 except UnicodeDecodeError: 

59 # Typeshed does not include find_config_files() for some reason. 

60 paths = d.find_config_files() # type: ignore 

61 logger.warning( 

62 "Ignore distutils configs in %s due to encoding errors.", 

63 ", ".join(os.path.basename(p) for p in paths), 

64 ) 

65 obj: Optional[DistutilsCommand] = None 

66 obj = d.get_command_obj("install", create=True) 

67 assert obj is not None 

68 i = cast(distutils_install_command, obj) 

69 # NOTE: setting user or home has the side-effect of creating the home dir 

70 # or user base for installations during finalize_options() 

71 # ideally, we'd prefer a scheme class that has no side-effects. 

72 assert not (user and prefix), f"user={user} prefix={prefix}" 

73 assert not (home and prefix), f"home={home} prefix={prefix}" 

74 i.user = user or i.user 

75 if user or home: 

76 i.prefix = "" 

77 i.prefix = prefix or i.prefix 

78 i.home = home or i.home 

79 i.root = root or i.root 

80 i.finalize_options() 

81 

82 scheme = {} 

83 for key in SCHEME_KEYS: 

84 scheme[key] = getattr(i, "install_" + key) 

85 

86 # install_lib specified in setup.cfg should install *everything* 

87 # into there (i.e. it takes precedence over both purelib and 

88 # platlib). Note, i.install_lib is *always* set after 

89 # finalize_options(); we only want to override here if the user 

90 # has explicitly requested it hence going back to the config 

91 if "install_lib" in d.get_option_dict("install"): 

92 scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib)) 

93 

94 if running_under_virtualenv(): 

95 if home: 

96 prefix = home 

97 elif user: 

98 prefix = i.install_userbase 

99 else: 

100 prefix = i.prefix 

101 scheme["headers"] = os.path.join( 

102 prefix, 

103 "include", 

104 "site", 

105 f"python{get_major_minor_version()}", 

106 dist_name, 

107 ) 

108 

109 if root is not None: 

110 path_no_drive = os.path.splitdrive(os.path.abspath(scheme["headers"]))[1] 

111 scheme["headers"] = os.path.join(root, path_no_drive[1:]) 

112 

113 return scheme 

114 

115 

116def get_scheme( 

117 dist_name: str, 

118 user: bool = False, 

119 home: Optional[str] = None, 

120 root: Optional[str] = None, 

121 isolated: bool = False, 

122 prefix: Optional[str] = None, 

123) -> Scheme: 

124 """ 

125 Get the "scheme" corresponding to the input parameters. The distutils 

126 documentation provides the context for the available schemes: 

127 https://docs.python.org/3/install/index.html#alternate-installation 

128 

129 :param dist_name: the name of the package to retrieve the scheme for, used 

130 in the headers scheme path 

131 :param user: indicates to use the "user" scheme 

132 :param home: indicates to use the "home" scheme and provides the base 

133 directory for the same 

134 :param root: root under which other directories are re-based 

135 :param isolated: equivalent to --no-user-cfg, i.e. do not consider 

136 ~/.pydistutils.cfg (posix) or ~/pydistutils.cfg (non-posix) for 

137 scheme paths 

138 :param prefix: indicates to use the "prefix" scheme and provides the 

139 base directory for the same 

140 """ 

141 scheme = distutils_scheme(dist_name, user, home, root, isolated, prefix) 

142 return Scheme( 

143 platlib=scheme["platlib"], 

144 purelib=scheme["purelib"], 

145 headers=scheme["headers"], 

146 scripts=scheme["scripts"], 

147 data=scheme["data"], 

148 ) 

149 

150 

151def get_bin_prefix() -> str: 

152 # XXX: In old virtualenv versions, sys.prefix can contain '..' components, 

153 # so we need to call normpath to eliminate them. 

154 prefix = os.path.normpath(sys.prefix) 

155 if WINDOWS: 

156 bin_py = os.path.join(prefix, "Scripts") 

157 # buildout uses 'bin' on Windows too? 

158 if not os.path.exists(bin_py): 

159 bin_py = os.path.join(prefix, "bin") 

160 return bin_py 

161 # Forcing to use /usr/local/bin for standard macOS framework installs 

162 # Also log to ~/Library/Logs/ for use with the Console.app log viewer 

163 if sys.platform[:6] == "darwin" and prefix[:16] == "/System/Library/": 

164 return "/usr/local/bin" 

165 return os.path.join(prefix, "bin") 

166 

167 

168def get_purelib() -> str: 

169 return get_python_lib(plat_specific=False) 

170 

171 

172def get_platlib() -> str: 

173 return get_python_lib(plat_specific=True)