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
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-03 02:21 +0200
1"""
2@file
3@brief Helpers for pip
5Some links to look:
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"""
12class PQPipError(Exception):
13 """
14 Any exception raised by one of the following function.
15 """
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)
29class Distribution:
30 """
31 Common interface for old and recent pip packages.
32 """
34 def __init__(self, dist):
35 self.dist = dist
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
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`.
73 Return a list of installed Distribution objects.
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)))
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)))
113def get_packages_list():
114 """
115 calls ``pip list`` to retrieve the list of packages
116 """
117 return get_installed_distributions(local_only=True)
120def package2dict(pkg):
121 """
122 Extracts information from a package.
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)
137def get_package_info(name=None, start=0, end=-1):
138 """
139 Calls ``pip show`` to retrieve information about packages.
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
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]