Coverage for pyquickhelper/pycode/pip_helper.py: 76%

63 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-03 02:21 +0200

1""" 

2@file 

3@brief Helpers for pip 

4 

5Some links to look: 

6 

7* `installing_python_packages_programatically.py <https://gist.github.com/rwilcox/755524>`_ 

8* `Calling pip programmatically <http://blog.ducky.io/python/2013/08/22/calling-pip-programmatically/>`_ 

9""" 

10 

11 

12class PQPipError(Exception): 

13 """ 

14 Any exception raised by one of the following function. 

15 """ 

16 

17 def __init__(self, *args): 

18 """ 

19 @param args either a string 3 strings (cmd, out, err) 

20 """ 

21 if len(args) == 1: 

22 Exception.__init__(self, args[0]) # pragma: no cover 

23 else: 

24 cmd, out, err = args 

25 mes = f"CMD:\n{cmd}\nOUT:\n{out}\n[piperror]\n{err}" 

26 Exception.__init__(self, mes) 

27 

28 

29class Distribution: 

30 """ 

31 Common interface for old and recent pip packages. 

32 """ 

33 

34 def __init__(self, dist): 

35 self.dist = dist 

36 

37 def __getattr__(self, attr): 

38 if attr == 'key': 

39 if hasattr(self.__dict__['dist'], 'key'): 

40 return self.__dict__['dist'].key 

41 return self.__dict__['dist'].canonical_name 

42 if attr == 'dist': 

43 return self.__dict__['dist'] 

44 if attr in {'_get_metadata', 'requires', 'PKG_INFO', 'project_name', 

45 'py_version', 'platform', 'extras'}: 

46 if hasattr(self.__dict__['dist'], attr): 

47 return getattr(self.__dict__['dist'], attr) 

48 try: 

49 return getattr(self.__dict__['dist']._dist, attr) 

50 except AttributeError as e: 

51 if attr == 'project_name': 

52 return getattr(self.__dict__['dist']._dist, 'name') 

53 if attr == 'py_version': 

54 return getattr(self.__dict__['dist']._dist, 'version') 

55 if attr in {'platform', 'extras'}: 

56 return None 

57 raise AttributeError( 

58 f"Unable to find {attr!r} in {dir(self.__dict__['dist']._dist)} or " 

59 f"{dir(self.__dict__['dist'])}.") from e 

60 try: 

61 return getattr(self.__dict__['dist'], attr) 

62 except AttributeError as e: 

63 raise AttributeError( 

64 f"Unable to find {attr!r} in {dir(self.__dict__['dist'])}.") from e 

65 

66 

67def get_installed_distributions(local_only=True, skip=None, 

68 include_editables=True, editables_only=False, 

69 user_only=False, use_cmd=False): 

70 """ 

71 Directs call to function *get_installed_distributions* from :epkg:`pip`. 

72 

73 Return a list of installed Distribution objects. 

74 

75 :param local_only: if True (default), only return installations 

76 local to the current virtualenv, if in a virtualenv. 

77 :param skip: argument is an iterable of lower-case project names to 

78 ignore; defaults to ``pip.compat.stdlib_pkgs`` (if *skip* is None) 

79 :param editables: if False, don't report editables. 

80 :param editables_only: if True , only report editables. 

81 :param user_only: if True , only report installations in the user 

82 site directory. 

83 :param use_cmd: if True, use a different process (updated package list) 

84 :return: list of installed Distribution objects. 

85 """ 

86 if use_cmd: 

87 raise NotImplementedError( # pragma: no cover 

88 "use_cmd should be False.") 

89 if skip is None: 

90 try: 

91 from pip._internal.utils.compat import stdlib_pkgs 

92 skip = stdlib_pkgs 

93 except ImportError: # pragma: no cover 

94 pass 

95 try: 

96 from pip._internal.metadata import get_default_environment 

97 return list(map(Distribution, 

98 get_default_environment().iter_installed_distributions( 

99 local_only=local_only, skip=skip, 

100 include_editables=include_editables, 

101 editables_only=editables_only, 

102 user_only=user_only))) 

103 

104 except ImportError: # pragma: no cover 

105 from pip._internal.utils.misc import get_installed_distributions as getd 

106 return list(map(Distribution, getd( 

107 local_only=local_only, skip=skip, 

108 include_editables=include_editables, 

109 editables_only=editables_only, 

110 user_only=user_only, use_cmd=use_cmd))) 

111 

112 

113def get_packages_list(): 

114 """ 

115 calls ``pip list`` to retrieve the list of packages 

116 """ 

117 return get_installed_distributions(local_only=True) 

118 

119 

120def package2dict(pkg): 

121 """ 

122 Extracts information from a package. 

123 

124 @param pkg type *pip._vendor.pkg_resources.Distribution* 

125 @return dictionary 

126 """ 

127 return dict( 

128 version=pkg.version, 

129 project_name=pkg.project_name, 

130 py_version=pkg.py_version, 

131 requires=pkg.requires, 

132 platform=pkg.platform, 

133 extras=pkg.extras, 

134 location=pkg.location) 

135 

136 

137def get_package_info(name=None, start=0, end=-1): 

138 """ 

139 Calls ``pip show`` to retrieve information about packages. 

140 

141 @param name name of he packages or None to get all of them in a list 

142 @param start start at package n (in list return by @see fn get_packages_list) 

143 @param end end at package n, -1 for all 

144 @return dictionary or list of dictionaries 

145 """ 

146 from pip._internal.commands.show import search_packages_info 

147 if name is None: 

148 res = [] 

149 packs = get_packages_list() 

150 if end == -1: 

151 end = len(packs) # pragma: no cover 

152 subp = packs[start:end] 

153 if len(subp) == 0: 

154 raise PQPipError( # pragma: no cover 

155 "No package, start={0}, end={1}, len(subp)={2}, len(packs)={3}".format( 

156 start, end, len(subp), len(packs))) 

157 for cp in subp: 

158 pack = cp.project_name 

159 info = get_package_info(pack) 

160 res.append(info) 

161 if len(res) == 0 and len(subp) > 0: 

162 raise PQPipError( # pragma: no cover 

163 "Empty list, unexpected, start={0}, end={1}, len(subp)={3}".format( 

164 start, end, len(subp))) 

165 return res 

166 

167 res = list(search_packages_info([name])) 

168 if len(res) != 1: 

169 raise PQPipError( # pragma: no cover 

170 f"Unexpected number of results {len(res)} for {name}") 

171 return res[0]