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.4.3, created at 2024-02-26 06:33 +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 paths = d.find_config_files() 

60 logger.warning( 

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

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

63 ) 

64 obj: Optional[DistutilsCommand] = None 

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

66 assert obj is not None 

67 i = cast(distutils_install_command, obj) 

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

69 # or user base for installations during finalize_options() 

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

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

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

73 i.user = user or i.user 

74 if user or home: 

75 i.prefix = "" 

76 i.prefix = prefix or i.prefix 

77 i.home = home or i.home 

78 i.root = root or i.root 

79 i.finalize_options() 

80 

81 scheme = {} 

82 for key in SCHEME_KEYS: 

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

84 

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

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

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

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

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

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

91 scheme.update({"purelib": i.install_lib, "platlib": i.install_lib}) 

92 

93 if running_under_virtualenv(): 

94 if home: 

95 prefix = home 

96 elif user: 

97 prefix = i.install_userbase 

98 else: 

99 prefix = i.prefix 

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

101 prefix, 

102 "include", 

103 "site", 

104 f"python{get_major_minor_version()}", 

105 dist_name, 

106 ) 

107 

108 if root is not None: 

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

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

111 

112 return scheme 

113 

114 

115def get_scheme( 

116 dist_name: str, 

117 user: bool = False, 

118 home: Optional[str] = None, 

119 root: Optional[str] = None, 

120 isolated: bool = False, 

121 prefix: Optional[str] = None, 

122) -> Scheme: 

123 """ 

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

125 documentation provides the context for the available schemes: 

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

127 

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

129 in the headers scheme path 

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

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

132 directory for the same 

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

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

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

136 scheme paths 

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

138 base directory for the same 

139 """ 

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

141 return Scheme( 

142 platlib=scheme["platlib"], 

143 purelib=scheme["purelib"], 

144 headers=scheme["headers"], 

145 scripts=scheme["scripts"], 

146 data=scheme["data"], 

147 ) 

148 

149 

150def get_bin_prefix() -> str: 

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

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

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

154 if WINDOWS: 

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

156 # buildout uses 'bin' on Windows too? 

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

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

159 return bin_py 

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

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

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

163 return "/usr/local/bin" 

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

165 

166 

167def get_purelib() -> str: 

168 return get_python_lib(plat_specific=False) 

169 

170 

171def get_platlib() -> str: 

172 return get_python_lib(plat_specific=True)