diff options
Diffstat (limited to 'venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distro.py')
-rw-r--r-- | venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distro.py | 1104 |
1 files changed, 0 insertions, 1104 deletions
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distro.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distro.py deleted file mode 100644 index 0f792ea..0000000 --- a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distro.py +++ /dev/null | |||
@@ -1,1104 +0,0 @@ | |||
1 | # Copyright 2015,2016 Nir Cohen | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | # you may not use this file except in compliance with the License. | ||
5 | # You may obtain a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | # See the License for the specific language governing permissions and | ||
13 | # limitations under the License. | ||
14 | |||
15 | """ | ||
16 | The ``distro`` package (``distro`` stands for Linux Distribution) provides | ||
17 | information about the Linux distribution it runs on, such as a reliable | ||
18 | machine-readable distro ID, or version information. | ||
19 | |||
20 | It is a renewed alternative implementation for Python's original | ||
21 | :py:func:`platform.linux_distribution` function, but it provides much more | ||
22 | functionality. An alternative implementation became necessary because Python | ||
23 | 3.5 deprecated this function, and Python 3.7 is expected to remove it | ||
24 | altogether. Its predecessor function :py:func:`platform.dist` was already | ||
25 | deprecated since Python 2.6 and is also expected to be removed in Python 3.7. | ||
26 | Still, there are many cases in which access to Linux distribution information | ||
27 | is needed. See `Python issue 1322 <https://bugs.python.org/issue1322>`_ for | ||
28 | more information. | ||
29 | """ | ||
30 | |||
31 | import os | ||
32 | import re | ||
33 | import sys | ||
34 | import json | ||
35 | import shlex | ||
36 | import logging | ||
37 | import argparse | ||
38 | import subprocess | ||
39 | |||
40 | |||
41 | _UNIXCONFDIR = os.environ.get('UNIXCONFDIR', '/etc') | ||
42 | _OS_RELEASE_BASENAME = 'os-release' | ||
43 | |||
44 | #: Translation table for normalizing the "ID" attribute defined in os-release | ||
45 | #: files, for use by the :func:`distro.id` method. | ||
46 | #: | ||
47 | #: * Key: Value as defined in the os-release file, translated to lower case, | ||
48 | #: with blanks translated to underscores. | ||
49 | #: | ||
50 | #: * Value: Normalized value. | ||
51 | NORMALIZED_OS_ID = {} | ||
52 | |||
53 | #: Translation table for normalizing the "Distributor ID" attribute returned by | ||
54 | #: the lsb_release command, for use by the :func:`distro.id` method. | ||
55 | #: | ||
56 | #: * Key: Value as returned by the lsb_release command, translated to lower | ||
57 | #: case, with blanks translated to underscores. | ||
58 | #: | ||
59 | #: * Value: Normalized value. | ||
60 | NORMALIZED_LSB_ID = { | ||
61 | 'enterpriseenterprise': 'oracle', # Oracle Enterprise Linux | ||
62 | 'redhatenterpriseworkstation': 'rhel', # RHEL 6, 7 Workstation | ||
63 | 'redhatenterpriseserver': 'rhel', # RHEL 6, 7 Server | ||
64 | } | ||
65 | |||
66 | #: Translation table for normalizing the distro ID derived from the file name | ||
67 | #: of distro release files, for use by the :func:`distro.id` method. | ||
68 | #: | ||
69 | #: * Key: Value as derived from the file name of a distro release file, | ||
70 | #: translated to lower case, with blanks translated to underscores. | ||
71 | #: | ||
72 | #: * Value: Normalized value. | ||
73 | NORMALIZED_DISTRO_ID = { | ||
74 | 'redhat': 'rhel', # RHEL 6.x, 7.x | ||
75 | } | ||
76 | |||
77 | # Pattern for content of distro release file (reversed) | ||
78 | _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( | ||
79 | r'(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)') | ||
80 | |||
81 | # Pattern for base file name of distro release file | ||
82 | _DISTRO_RELEASE_BASENAME_PATTERN = re.compile( | ||
83 | r'(\w+)[-_](release|version)$') | ||
84 | |||
85 | # Base file names to be ignored when searching for distro release file | ||
86 | _DISTRO_RELEASE_IGNORE_BASENAMES = ( | ||
87 | 'debian_version', | ||
88 | 'lsb-release', | ||
89 | 'oem-release', | ||
90 | _OS_RELEASE_BASENAME, | ||
91 | 'system-release' | ||
92 | ) | ||
93 | |||
94 | |||
95 | def linux_distribution(full_distribution_name=True): | ||
96 | """ | ||
97 | Return information about the current Linux distribution as a tuple | ||
98 | ``(id_name, version, codename)`` with items as follows: | ||
99 | |||
100 | * ``id_name``: If *full_distribution_name* is false, the result of | ||
101 | :func:`distro.id`. Otherwise, the result of :func:`distro.name`. | ||
102 | |||
103 | * ``version``: The result of :func:`distro.version`. | ||
104 | |||
105 | * ``codename``: The result of :func:`distro.codename`. | ||
106 | |||
107 | The interface of this function is compatible with the original | ||
108 | :py:func:`platform.linux_distribution` function, supporting a subset of | ||
109 | its parameters. | ||
110 | |||
111 | The data it returns may not exactly be the same, because it uses more data | ||
112 | sources than the original function, and that may lead to different data if | ||
113 | the Linux distribution is not consistent across multiple data sources it | ||
114 | provides (there are indeed such distributions ...). | ||
115 | |||
116 | Another reason for differences is the fact that the :func:`distro.id` | ||
117 | method normalizes the distro ID string to a reliable machine-readable value | ||
118 | for a number of popular Linux distributions. | ||
119 | """ | ||
120 | return _distro.linux_distribution(full_distribution_name) | ||
121 | |||
122 | |||
123 | def id(): | ||
124 | """ | ||
125 | Return the distro ID of the current Linux distribution, as a | ||
126 | machine-readable string. | ||
127 | |||
128 | For a number of Linux distributions, the returned distro ID value is | ||
129 | *reliable*, in the sense that it is documented and that it does not change | ||
130 | across releases of the distribution. | ||
131 | |||
132 | This package maintains the following reliable distro ID values: | ||
133 | |||
134 | ============== ========================================= | ||
135 | Distro ID Distribution | ||
136 | ============== ========================================= | ||
137 | "ubuntu" Ubuntu | ||
138 | "debian" Debian | ||
139 | "rhel" RedHat Enterprise Linux | ||
140 | "centos" CentOS | ||
141 | "fedora" Fedora | ||
142 | "sles" SUSE Linux Enterprise Server | ||
143 | "opensuse" openSUSE | ||
144 | "amazon" Amazon Linux | ||
145 | "arch" Arch Linux | ||
146 | "cloudlinux" CloudLinux OS | ||
147 | "exherbo" Exherbo Linux | ||
148 | "gentoo" GenToo Linux | ||
149 | "ibm_powerkvm" IBM PowerKVM | ||
150 | "kvmibm" KVM for IBM z Systems | ||
151 | "linuxmint" Linux Mint | ||
152 | "mageia" Mageia | ||
153 | "mandriva" Mandriva Linux | ||
154 | "parallels" Parallels | ||
155 | "pidora" Pidora | ||
156 | "raspbian" Raspbian | ||
157 | "oracle" Oracle Linux (and Oracle Enterprise Linux) | ||
158 | "scientific" Scientific Linux | ||
159 | "slackware" Slackware | ||
160 | "xenserver" XenServer | ||
161 | ============== ========================================= | ||
162 | |||
163 | If you have a need to get distros for reliable IDs added into this set, | ||
164 | or if you find that the :func:`distro.id` function returns a different | ||
165 | distro ID for one of the listed distros, please create an issue in the | ||
166 | `distro issue tracker`_. | ||
167 | |||
168 | **Lookup hierarchy and transformations:** | ||
169 | |||
170 | First, the ID is obtained from the following sources, in the specified | ||
171 | order. The first available and non-empty value is used: | ||
172 | |||
173 | * the value of the "ID" attribute of the os-release file, | ||
174 | |||
175 | * the value of the "Distributor ID" attribute returned by the lsb_release | ||
176 | command, | ||
177 | |||
178 | * the first part of the file name of the distro release file, | ||
179 | |||
180 | The so determined ID value then passes the following transformations, | ||
181 | before it is returned by this method: | ||
182 | |||
183 | * it is translated to lower case, | ||
184 | |||
185 | * blanks (which should not be there anyway) are translated to underscores, | ||
186 | |||
187 | * a normalization of the ID is performed, based upon | ||
188 | `normalization tables`_. The purpose of this normalization is to ensure | ||
189 | that the ID is as reliable as possible, even across incompatible changes | ||
190 | in the Linux distributions. A common reason for an incompatible change is | ||
191 | the addition of an os-release file, or the addition of the lsb_release | ||
192 | command, with ID values that differ from what was previously determined | ||
193 | from the distro release file name. | ||
194 | """ | ||
195 | return _distro.id() | ||
196 | |||
197 | |||
198 | def name(pretty=False): | ||
199 | """ | ||
200 | Return the name of the current Linux distribution, as a human-readable | ||
201 | string. | ||
202 | |||
203 | If *pretty* is false, the name is returned without version or codename. | ||
204 | (e.g. "CentOS Linux") | ||
205 | |||
206 | If *pretty* is true, the version and codename are appended. | ||
207 | (e.g. "CentOS Linux 7.1.1503 (Core)") | ||
208 | |||
209 | **Lookup hierarchy:** | ||
210 | |||
211 | The name is obtained from the following sources, in the specified order. | ||
212 | The first available and non-empty value is used: | ||
213 | |||
214 | * If *pretty* is false: | ||
215 | |||
216 | - the value of the "NAME" attribute of the os-release file, | ||
217 | |||
218 | - the value of the "Distributor ID" attribute returned by the lsb_release | ||
219 | command, | ||
220 | |||
221 | - the value of the "<name>" field of the distro release file. | ||
222 | |||
223 | * If *pretty* is true: | ||
224 | |||
225 | - the value of the "PRETTY_NAME" attribute of the os-release file, | ||
226 | |||
227 | - the value of the "Description" attribute returned by the lsb_release | ||
228 | command, | ||
229 | |||
230 | - the value of the "<name>" field of the distro release file, appended | ||
231 | with the value of the pretty version ("<version_id>" and "<codename>" | ||
232 | fields) of the distro release file, if available. | ||
233 | """ | ||
234 | return _distro.name(pretty) | ||
235 | |||
236 | |||
237 | def version(pretty=False, best=False): | ||
238 | """ | ||
239 | Return the version of the current Linux distribution, as a human-readable | ||
240 | string. | ||
241 | |||
242 | If *pretty* is false, the version is returned without codename (e.g. | ||
243 | "7.0"). | ||
244 | |||
245 | If *pretty* is true, the codename in parenthesis is appended, if the | ||
246 | codename is non-empty (e.g. "7.0 (Maipo)"). | ||
247 | |||
248 | Some distributions provide version numbers with different precisions in | ||
249 | the different sources of distribution information. Examining the different | ||
250 | sources in a fixed priority order does not always yield the most precise | ||
251 | version (e.g. for Debian 8.2, or CentOS 7.1). | ||
252 | |||
253 | The *best* parameter can be used to control the approach for the returned | ||
254 | version: | ||
255 | |||
256 | If *best* is false, the first non-empty version number in priority order of | ||
257 | the examined sources is returned. | ||
258 | |||
259 | If *best* is true, the most precise version number out of all examined | ||
260 | sources is returned. | ||
261 | |||
262 | **Lookup hierarchy:** | ||
263 | |||
264 | In all cases, the version number is obtained from the following sources. | ||
265 | If *best* is false, this order represents the priority order: | ||
266 | |||
267 | * the value of the "VERSION_ID" attribute of the os-release file, | ||
268 | * the value of the "Release" attribute returned by the lsb_release | ||
269 | command, | ||
270 | * the version number parsed from the "<version_id>" field of the first line | ||
271 | of the distro release file, | ||
272 | * the version number parsed from the "PRETTY_NAME" attribute of the | ||
273 | os-release file, if it follows the format of the distro release files. | ||
274 | * the version number parsed from the "Description" attribute returned by | ||
275 | the lsb_release command, if it follows the format of the distro release | ||
276 | files. | ||
277 | """ | ||
278 | return _distro.version(pretty, best) | ||
279 | |||
280 | |||
281 | def version_parts(best=False): | ||
282 | """ | ||
283 | Return the version of the current Linux distribution as a tuple | ||
284 | ``(major, minor, build_number)`` with items as follows: | ||
285 | |||
286 | * ``major``: The result of :func:`distro.major_version`. | ||
287 | |||
288 | * ``minor``: The result of :func:`distro.minor_version`. | ||
289 | |||
290 | * ``build_number``: The result of :func:`distro.build_number`. | ||
291 | |||
292 | For a description of the *best* parameter, see the :func:`distro.version` | ||
293 | method. | ||
294 | """ | ||
295 | return _distro.version_parts(best) | ||
296 | |||
297 | |||
298 | def major_version(best=False): | ||
299 | """ | ||
300 | Return the major version of the current Linux distribution, as a string, | ||
301 | if provided. | ||
302 | Otherwise, the empty string is returned. The major version is the first | ||
303 | part of the dot-separated version string. | ||
304 | |||
305 | For a description of the *best* parameter, see the :func:`distro.version` | ||
306 | method. | ||
307 | """ | ||
308 | return _distro.major_version(best) | ||
309 | |||
310 | |||
311 | def minor_version(best=False): | ||
312 | """ | ||
313 | Return the minor version of the current Linux distribution, as a string, | ||
314 | if provided. | ||
315 | Otherwise, the empty string is returned. The minor version is the second | ||
316 | part of the dot-separated version string. | ||
317 | |||
318 | For a description of the *best* parameter, see the :func:`distro.version` | ||
319 | method. | ||
320 | """ | ||
321 | return _distro.minor_version(best) | ||
322 | |||
323 | |||
324 | def build_number(best=False): | ||
325 | """ | ||
326 | Return the build number of the current Linux distribution, as a string, | ||
327 | if provided. | ||
328 | Otherwise, the empty string is returned. The build number is the third part | ||
329 | of the dot-separated version string. | ||
330 | |||
331 | For a description of the *best* parameter, see the :func:`distro.version` | ||
332 | method. | ||
333 | """ | ||
334 | return _distro.build_number(best) | ||
335 | |||
336 | |||
337 | def like(): | ||
338 | """ | ||
339 | Return a space-separated list of distro IDs of distributions that are | ||
340 | closely related to the current Linux distribution in regards to packaging | ||
341 | and programming interfaces, for example distributions the current | ||
342 | distribution is a derivative from. | ||
343 | |||
344 | **Lookup hierarchy:** | ||
345 | |||
346 | This information item is only provided by the os-release file. | ||
347 | For details, see the description of the "ID_LIKE" attribute in the | ||
348 | `os-release man page | ||
349 | <http://www.freedesktop.org/software/systemd/man/os-release.html>`_. | ||
350 | """ | ||
351 | return _distro.like() | ||
352 | |||
353 | |||
354 | def codename(): | ||
355 | """ | ||
356 | Return the codename for the release of the current Linux distribution, | ||
357 | as a string. | ||
358 | |||
359 | If the distribution does not have a codename, an empty string is returned. | ||
360 | |||
361 | Note that the returned codename is not always really a codename. For | ||
362 | example, openSUSE returns "x86_64". This function does not handle such | ||
363 | cases in any special way and just returns the string it finds, if any. | ||
364 | |||
365 | **Lookup hierarchy:** | ||
366 | |||
367 | * the codename within the "VERSION" attribute of the os-release file, if | ||
368 | provided, | ||
369 | |||
370 | * the value of the "Codename" attribute returned by the lsb_release | ||
371 | command, | ||
372 | |||
373 | * the value of the "<codename>" field of the distro release file. | ||
374 | """ | ||
375 | return _distro.codename() | ||
376 | |||
377 | |||
378 | def info(pretty=False, best=False): | ||
379 | """ | ||
380 | Return certain machine-readable information items about the current Linux | ||
381 | distribution in a dictionary, as shown in the following example: | ||
382 | |||
383 | .. sourcecode:: python | ||
384 | |||
385 | { | ||
386 | 'id': 'rhel', | ||
387 | 'version': '7.0', | ||
388 | 'version_parts': { | ||
389 | 'major': '7', | ||
390 | 'minor': '0', | ||
391 | 'build_number': '' | ||
392 | }, | ||
393 | 'like': 'fedora', | ||
394 | 'codename': 'Maipo' | ||
395 | } | ||
396 | |||
397 | The dictionary structure and keys are always the same, regardless of which | ||
398 | information items are available in the underlying data sources. The values | ||
399 | for the various keys are as follows: | ||
400 | |||
401 | * ``id``: The result of :func:`distro.id`. | ||
402 | |||
403 | * ``version``: The result of :func:`distro.version`. | ||
404 | |||
405 | * ``version_parts -> major``: The result of :func:`distro.major_version`. | ||
406 | |||
407 | * ``version_parts -> minor``: The result of :func:`distro.minor_version`. | ||
408 | |||
409 | * ``version_parts -> build_number``: The result of | ||
410 | :func:`distro.build_number`. | ||
411 | |||
412 | * ``like``: The result of :func:`distro.like`. | ||
413 | |||
414 | * ``codename``: The result of :func:`distro.codename`. | ||
415 | |||
416 | For a description of the *pretty* and *best* parameters, see the | ||
417 | :func:`distro.version` method. | ||
418 | """ | ||
419 | return _distro.info(pretty, best) | ||
420 | |||
421 | |||
422 | def os_release_info(): | ||
423 | """ | ||
424 | Return a dictionary containing key-value pairs for the information items | ||
425 | from the os-release file data source of the current Linux distribution. | ||
426 | |||
427 | See `os-release file`_ for details about these information items. | ||
428 | """ | ||
429 | return _distro.os_release_info() | ||
430 | |||
431 | |||
432 | def lsb_release_info(): | ||
433 | """ | ||
434 | Return a dictionary containing key-value pairs for the information items | ||
435 | from the lsb_release command data source of the current Linux distribution. | ||
436 | |||
437 | See `lsb_release command output`_ for details about these information | ||
438 | items. | ||
439 | """ | ||
440 | return _distro.lsb_release_info() | ||
441 | |||
442 | |||
443 | def distro_release_info(): | ||
444 | """ | ||
445 | Return a dictionary containing key-value pairs for the information items | ||
446 | from the distro release file data source of the current Linux distribution. | ||
447 | |||
448 | See `distro release file`_ for details about these information items. | ||
449 | """ | ||
450 | return _distro.distro_release_info() | ||
451 | |||
452 | |||
453 | def os_release_attr(attribute): | ||
454 | """ | ||
455 | Return a single named information item from the os-release file data source | ||
456 | of the current Linux distribution. | ||
457 | |||
458 | Parameters: | ||
459 | |||
460 | * ``attribute`` (string): Key of the information item. | ||
461 | |||
462 | Returns: | ||
463 | |||
464 | * (string): Value of the information item, if the item exists. | ||
465 | The empty string, if the item does not exist. | ||
466 | |||
467 | See `os-release file`_ for details about these information items. | ||
468 | """ | ||
469 | return _distro.os_release_attr(attribute) | ||
470 | |||
471 | |||
472 | def lsb_release_attr(attribute): | ||
473 | """ | ||
474 | Return a single named information item from the lsb_release command output | ||
475 | data source of the current Linux distribution. | ||
476 | |||
477 | Parameters: | ||
478 | |||
479 | * ``attribute`` (string): Key of the information item. | ||
480 | |||
481 | Returns: | ||
482 | |||
483 | * (string): Value of the information item, if the item exists. | ||
484 | The empty string, if the item does not exist. | ||
485 | |||
486 | See `lsb_release command output`_ for details about these information | ||
487 | items. | ||
488 | """ | ||
489 | return _distro.lsb_release_attr(attribute) | ||
490 | |||
491 | |||
492 | def distro_release_attr(attribute): | ||
493 | """ | ||
494 | Return a single named information item from the distro release file | ||
495 | data source of the current Linux distribution. | ||
496 | |||
497 | Parameters: | ||
498 | |||
499 | * ``attribute`` (string): Key of the information item. | ||
500 | |||
501 | Returns: | ||
502 | |||
503 | * (string): Value of the information item, if the item exists. | ||
504 | The empty string, if the item does not exist. | ||
505 | |||
506 | See `distro release file`_ for details about these information items. | ||
507 | """ | ||
508 | return _distro.distro_release_attr(attribute) | ||
509 | |||
510 | |||
511 | class cached_property(object): | ||
512 | """A version of @property which caches the value. On access, it calls the | ||
513 | underlying function and sets the value in `__dict__` so future accesses | ||
514 | will not re-call the property. | ||
515 | """ | ||
516 | def __init__(self, f): | ||
517 | self._fname = f.__name__ | ||
518 | self._f = f | ||
519 | |||
520 | def __get__(self, obj, owner): | ||
521 | assert obj is not None, 'call {} on an instance'.format(self._fname) | ||
522 | ret = obj.__dict__[self._fname] = self._f(obj) | ||
523 | return ret | ||
524 | |||
525 | |||
526 | class LinuxDistribution(object): | ||
527 | """ | ||
528 | Provides information about a Linux distribution. | ||
529 | |||
530 | This package creates a private module-global instance of this class with | ||
531 | default initialization arguments, that is used by the | ||
532 | `consolidated accessor functions`_ and `single source accessor functions`_. | ||
533 | By using default initialization arguments, that module-global instance | ||
534 | returns data about the current Linux distribution (i.e. the distro this | ||
535 | package runs on). | ||
536 | |||
537 | Normally, it is not necessary to create additional instances of this class. | ||
538 | However, in situations where control is needed over the exact data sources | ||
539 | that are used, instances of this class can be created with a specific | ||
540 | distro release file, or a specific os-release file, or without invoking the | ||
541 | lsb_release command. | ||
542 | """ | ||
543 | |||
544 | def __init__(self, | ||
545 | include_lsb=True, | ||
546 | os_release_file='', | ||
547 | distro_release_file=''): | ||
548 | """ | ||
549 | The initialization method of this class gathers information from the | ||
550 | available data sources, and stores that in private instance attributes. | ||
551 | Subsequent access to the information items uses these private instance | ||
552 | attributes, so that the data sources are read only once. | ||
553 | |||
554 | Parameters: | ||
555 | |||
556 | * ``include_lsb`` (bool): Controls whether the | ||
557 | `lsb_release command output`_ is included as a data source. | ||
558 | |||
559 | If the lsb_release command is not available in the program execution | ||
560 | path, the data source for the lsb_release command will be empty. | ||
561 | |||
562 | * ``os_release_file`` (string): The path name of the | ||
563 | `os-release file`_ that is to be used as a data source. | ||
564 | |||
565 | An empty string (the default) will cause the default path name to | ||
566 | be used (see `os-release file`_ for details). | ||
567 | |||
568 | If the specified or defaulted os-release file does not exist, the | ||
569 | data source for the os-release file will be empty. | ||
570 | |||
571 | * ``distro_release_file`` (string): The path name of the | ||
572 | `distro release file`_ that is to be used as a data source. | ||
573 | |||
574 | An empty string (the default) will cause a default search algorithm | ||
575 | to be used (see `distro release file`_ for details). | ||
576 | |||
577 | If the specified distro release file does not exist, or if no default | ||
578 | distro release file can be found, the data source for the distro | ||
579 | release file will be empty. | ||
580 | |||
581 | Public instance attributes: | ||
582 | |||
583 | * ``os_release_file`` (string): The path name of the | ||
584 | `os-release file`_ that is actually used as a data source. The | ||
585 | empty string if no distro release file is used as a data source. | ||
586 | |||
587 | * ``distro_release_file`` (string): The path name of the | ||
588 | `distro release file`_ that is actually used as a data source. The | ||
589 | empty string if no distro release file is used as a data source. | ||
590 | |||
591 | * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter. | ||
592 | This controls whether the lsb information will be loaded. | ||
593 | |||
594 | Raises: | ||
595 | |||
596 | * :py:exc:`IOError`: Some I/O issue with an os-release file or distro | ||
597 | release file. | ||
598 | |||
599 | * :py:exc:`subprocess.CalledProcessError`: The lsb_release command had | ||
600 | some issue (other than not being available in the program execution | ||
601 | path). | ||
602 | |||
603 | * :py:exc:`UnicodeError`: A data source has unexpected characters or | ||
604 | uses an unexpected encoding. | ||
605 | """ | ||
606 | self.os_release_file = os_release_file or \ | ||
607 | os.path.join(_UNIXCONFDIR, _OS_RELEASE_BASENAME) | ||
608 | self.distro_release_file = distro_release_file or '' # updated later | ||
609 | self.include_lsb = include_lsb | ||
610 | |||
611 | def __repr__(self): | ||
612 | """Return repr of all info | ||
613 | """ | ||
614 | return \ | ||
615 | "LinuxDistribution(" \ | ||
616 | "os_release_file={self.os_release_file!r}, " \ | ||
617 | "distro_release_file={self.distro_release_file!r}, " \ | ||
618 | "include_lsb={self.include_lsb!r}, " \ | ||
619 | "_os_release_info={self._os_release_info!r}, " \ | ||
620 | "_lsb_release_info={self._lsb_release_info!r}, " \ | ||
621 | "_distro_release_info={self._distro_release_info!r})".format( | ||
622 | self=self) | ||
623 | |||
624 | def linux_distribution(self, full_distribution_name=True): | ||
625 | """ | ||
626 | Return information about the Linux distribution that is compatible | ||
627 | with Python's :func:`platform.linux_distribution`, supporting a subset | ||
628 | of its parameters. | ||
629 | |||
630 | For details, see :func:`distro.linux_distribution`. | ||
631 | """ | ||
632 | return ( | ||
633 | self.name() if full_distribution_name else self.id(), | ||
634 | self.version(), | ||
635 | self.codename() | ||
636 | ) | ||
637 | |||
638 | def id(self): | ||
639 | """Return the distro ID of the Linux distribution, as a string. | ||
640 | |||
641 | For details, see :func:`distro.id`. | ||
642 | """ | ||
643 | def normalize(distro_id, table): | ||
644 | distro_id = distro_id.lower().replace(' ', '_') | ||
645 | return table.get(distro_id, distro_id) | ||
646 | |||
647 | distro_id = self.os_release_attr('id') | ||
648 | if distro_id: | ||
649 | return normalize(distro_id, NORMALIZED_OS_ID) | ||
650 | |||
651 | distro_id = self.lsb_release_attr('distributor_id') | ||
652 | if distro_id: | ||
653 | return normalize(distro_id, NORMALIZED_LSB_ID) | ||
654 | |||
655 | distro_id = self.distro_release_attr('id') | ||
656 | if distro_id: | ||
657 | return normalize(distro_id, NORMALIZED_DISTRO_ID) | ||
658 | |||
659 | return '' | ||
660 | |||
661 | def name(self, pretty=False): | ||
662 | """ | ||
663 | Return the name of the Linux distribution, as a string. | ||
664 | |||
665 | For details, see :func:`distro.name`. | ||
666 | """ | ||
667 | name = self.os_release_attr('name') \ | ||
668 | or self.lsb_release_attr('distributor_id') \ | ||
669 | or self.distro_release_attr('name') | ||
670 | if pretty: | ||
671 | name = self.os_release_attr('pretty_name') \ | ||
672 | or self.lsb_release_attr('description') | ||
673 | if not name: | ||
674 | name = self.distro_release_attr('name') | ||
675 | version = self.version(pretty=True) | ||
676 | if version: | ||
677 | name = name + ' ' + version | ||
678 | return name or '' | ||
679 | |||
680 | def version(self, pretty=False, best=False): | ||
681 | """ | ||
682 | Return the version of the Linux distribution, as a string. | ||
683 | |||
684 | For details, see :func:`distro.version`. | ||
685 | """ | ||
686 | versions = [ | ||
687 | self.os_release_attr('version_id'), | ||
688 | self.lsb_release_attr('release'), | ||
689 | self.distro_release_attr('version_id'), | ||
690 | self._parse_distro_release_content( | ||
691 | self.os_release_attr('pretty_name')).get('version_id', ''), | ||
692 | self._parse_distro_release_content( | ||
693 | self.lsb_release_attr('description')).get('version_id', '') | ||
694 | ] | ||
695 | version = '' | ||
696 | if best: | ||
697 | # This algorithm uses the last version in priority order that has | ||
698 | # the best precision. If the versions are not in conflict, that | ||
699 | # does not matter; otherwise, using the last one instead of the | ||
700 | # first one might be considered a surprise. | ||
701 | for v in versions: | ||
702 | if v.count(".") > version.count(".") or version == '': | ||
703 | version = v | ||
704 | else: | ||
705 | for v in versions: | ||
706 | if v != '': | ||
707 | version = v | ||
708 | break | ||
709 | if pretty and version and self.codename(): | ||
710 | version = u'{0} ({1})'.format(version, self.codename()) | ||
711 | return version | ||
712 | |||
713 | def version_parts(self, best=False): | ||
714 | """ | ||
715 | Return the version of the Linux distribution, as a tuple of version | ||
716 | numbers. | ||
717 | |||
718 | For details, see :func:`distro.version_parts`. | ||
719 | """ | ||
720 | version_str = self.version(best=best) | ||
721 | if version_str: | ||
722 | version_regex = re.compile(r'(\d+)\.?(\d+)?\.?(\d+)?') | ||
723 | matches = version_regex.match(version_str) | ||
724 | if matches: | ||
725 | major, minor, build_number = matches.groups() | ||
726 | return major, minor or '', build_number or '' | ||
727 | return '', '', '' | ||
728 | |||
729 | def major_version(self, best=False): | ||
730 | """ | ||
731 | Return the major version number of the current distribution. | ||
732 | |||
733 | For details, see :func:`distro.major_version`. | ||
734 | """ | ||
735 | return self.version_parts(best)[0] | ||
736 | |||
737 | def minor_version(self, best=False): | ||
738 | """ | ||
739 | Return the minor version number of the Linux distribution. | ||
740 | |||
741 | For details, see :func:`distro.minor_version`. | ||
742 | """ | ||
743 | return self.version_parts(best)[1] | ||
744 | |||
745 | def build_number(self, best=False): | ||
746 | """ | ||
747 | Return the build number of the Linux distribution. | ||
748 | |||
749 | For details, see :func:`distro.build_number`. | ||
750 | """ | ||
751 | return self.version_parts(best)[2] | ||
752 | |||
753 | def like(self): | ||
754 | """ | ||
755 | Return the IDs of distributions that are like the Linux distribution. | ||
756 | |||
757 | For details, see :func:`distro.like`. | ||
758 | """ | ||
759 | return self.os_release_attr('id_like') or '' | ||
760 | |||
761 | def codename(self): | ||
762 | """ | ||
763 | Return the codename of the Linux distribution. | ||
764 | |||
765 | For details, see :func:`distro.codename`. | ||
766 | """ | ||
767 | return self.os_release_attr('codename') \ | ||
768 | or self.lsb_release_attr('codename') \ | ||
769 | or self.distro_release_attr('codename') \ | ||
770 | or '' | ||
771 | |||
772 | def info(self, pretty=False, best=False): | ||
773 | """ | ||
774 | Return certain machine-readable information about the Linux | ||
775 | distribution. | ||
776 | |||
777 | For details, see :func:`distro.info`. | ||
778 | """ | ||
779 | return dict( | ||
780 | id=self.id(), | ||
781 | version=self.version(pretty, best), | ||
782 | version_parts=dict( | ||
783 | major=self.major_version(best), | ||
784 | minor=self.minor_version(best), | ||
785 | build_number=self.build_number(best) | ||
786 | ), | ||
787 | like=self.like(), | ||
788 | codename=self.codename(), | ||
789 | ) | ||
790 | |||
791 | def os_release_info(self): | ||
792 | """ | ||
793 | Return a dictionary containing key-value pairs for the information | ||
794 | items from the os-release file data source of the Linux distribution. | ||
795 | |||
796 | For details, see :func:`distro.os_release_info`. | ||
797 | """ | ||
798 | return self._os_release_info | ||
799 | |||
800 | def lsb_release_info(self): | ||
801 | """ | ||
802 | Return a dictionary containing key-value pairs for the information | ||
803 | items from the lsb_release command data source of the Linux | ||
804 | distribution. | ||
805 | |||
806 | For details, see :func:`distro.lsb_release_info`. | ||
807 | """ | ||
808 | return self._lsb_release_info | ||
809 | |||
810 | def distro_release_info(self): | ||
811 | """ | ||
812 | Return a dictionary containing key-value pairs for the information | ||
813 | items from the distro release file data source of the Linux | ||
814 | distribution. | ||
815 | |||
816 | For details, see :func:`distro.distro_release_info`. | ||
817 | """ | ||
818 | return self._distro_release_info | ||
819 | |||
820 | def os_release_attr(self, attribute): | ||
821 | """ | ||
822 | Return a single named information item from the os-release file data | ||
823 | source of the Linux distribution. | ||
824 | |||
825 | For details, see :func:`distro.os_release_attr`. | ||
826 | """ | ||
827 | return self._os_release_info.get(attribute, '') | ||
828 | |||
829 | def lsb_release_attr(self, attribute): | ||
830 | """ | ||
831 | Return a single named information item from the lsb_release command | ||
832 | output data source of the Linux distribution. | ||
833 | |||
834 | For details, see :func:`distro.lsb_release_attr`. | ||
835 | """ | ||
836 | return self._lsb_release_info.get(attribute, '') | ||
837 | |||
838 | def distro_release_attr(self, attribute): | ||
839 | """ | ||
840 | Return a single named information item from the distro release file | ||
841 | data source of the Linux distribution. | ||
842 | |||
843 | For details, see :func:`distro.distro_release_attr`. | ||
844 | """ | ||
845 | return self._distro_release_info.get(attribute, '') | ||
846 | |||
847 | @cached_property | ||
848 | def _os_release_info(self): | ||
849 | """ | ||
850 | Get the information items from the specified os-release file. | ||
851 | |||
852 | Returns: | ||
853 | A dictionary containing all information items. | ||
854 | """ | ||
855 | if os.path.isfile(self.os_release_file): | ||
856 | with open(self.os_release_file) as release_file: | ||
857 | return self._parse_os_release_content(release_file) | ||
858 | return {} | ||
859 | |||
860 | @staticmethod | ||
861 | def _parse_os_release_content(lines): | ||
862 | """ | ||
863 | Parse the lines of an os-release file. | ||
864 | |||
865 | Parameters: | ||
866 | |||
867 | * lines: Iterable through the lines in the os-release file. | ||
868 | Each line must be a unicode string or a UTF-8 encoded byte | ||
869 | string. | ||
870 | |||
871 | Returns: | ||
872 | A dictionary containing all information items. | ||
873 | """ | ||
874 | props = {} | ||
875 | lexer = shlex.shlex(lines, posix=True) | ||
876 | lexer.whitespace_split = True | ||
877 | |||
878 | # The shlex module defines its `wordchars` variable using literals, | ||
879 | # making it dependent on the encoding of the Python source file. | ||
880 | # In Python 2.6 and 2.7, the shlex source file is encoded in | ||
881 | # 'iso-8859-1', and the `wordchars` variable is defined as a byte | ||
882 | # string. This causes a UnicodeDecodeError to be raised when the | ||
883 | # parsed content is a unicode object. The following fix resolves that | ||
884 | # (... but it should be fixed in shlex...): | ||
885 | if sys.version_info[0] == 2 and isinstance(lexer.wordchars, bytes): | ||
886 | lexer.wordchars = lexer.wordchars.decode('iso-8859-1') | ||
887 | |||
888 | tokens = list(lexer) | ||
889 | for token in tokens: | ||
890 | # At this point, all shell-like parsing has been done (i.e. | ||
891 | # comments processed, quotes and backslash escape sequences | ||
892 | # processed, multi-line values assembled, trailing newlines | ||
893 | # stripped, etc.), so the tokens are now either: | ||
894 | # * variable assignments: var=value | ||
895 | # * commands or their arguments (not allowed in os-release) | ||
896 | if '=' in token: | ||
897 | k, v = token.split('=', 1) | ||
898 | if isinstance(v, bytes): | ||
899 | v = v.decode('utf-8') | ||
900 | props[k.lower()] = v | ||
901 | if k == 'VERSION': | ||
902 | # this handles cases in which the codename is in | ||
903 | # the `(CODENAME)` (rhel, centos, fedora) format | ||
904 | # or in the `, CODENAME` format (Ubuntu). | ||
905 | codename = re.search(r'(\(\D+\))|,(\s+)?\D+', v) | ||
906 | if codename: | ||
907 | codename = codename.group() | ||
908 | codename = codename.strip('()') | ||
909 | codename = codename.strip(',') | ||
910 | codename = codename.strip() | ||
911 | # codename appears within paranthese. | ||
912 | props['codename'] = codename | ||
913 | else: | ||
914 | props['codename'] = '' | ||
915 | else: | ||
916 | # Ignore any tokens that are not variable assignments | ||
917 | pass | ||
918 | return props | ||
919 | |||
920 | @cached_property | ||
921 | def _lsb_release_info(self): | ||
922 | """ | ||
923 | Get the information items from the lsb_release command output. | ||
924 | |||
925 | Returns: | ||
926 | A dictionary containing all information items. | ||
927 | """ | ||
928 | if not self.include_lsb: | ||
929 | return {} | ||
930 | with open(os.devnull, 'w') as devnull: | ||
931 | try: | ||
932 | cmd = ('lsb_release', '-a') | ||
933 | stdout = subprocess.check_output(cmd, stderr=devnull) | ||
934 | except OSError: # Command not found | ||
935 | return {} | ||
936 | content = stdout.decode(sys.getfilesystemencoding()).splitlines() | ||
937 | return self._parse_lsb_release_content(content) | ||
938 | |||
939 | @staticmethod | ||
940 | def _parse_lsb_release_content(lines): | ||
941 | """ | ||
942 | Parse the output of the lsb_release command. | ||
943 | |||
944 | Parameters: | ||
945 | |||
946 | * lines: Iterable through the lines of the lsb_release output. | ||
947 | Each line must be a unicode string or a UTF-8 encoded byte | ||
948 | string. | ||
949 | |||
950 | Returns: | ||
951 | A dictionary containing all information items. | ||
952 | """ | ||
953 | props = {} | ||
954 | for line in lines: | ||
955 | kv = line.strip('\n').split(':', 1) | ||
956 | if len(kv) != 2: | ||
957 | # Ignore lines without colon. | ||
958 | continue | ||
959 | k, v = kv | ||
960 | props.update({k.replace(' ', '_').lower(): v.strip()}) | ||
961 | return props | ||
962 | |||
963 | @cached_property | ||
964 | def _distro_release_info(self): | ||
965 | """ | ||
966 | Get the information items from the specified distro release file. | ||
967 | |||
968 | Returns: | ||
969 | A dictionary containing all information items. | ||
970 | """ | ||
971 | if self.distro_release_file: | ||
972 | # If it was specified, we use it and parse what we can, even if | ||
973 | # its file name or content does not match the expected pattern. | ||
974 | distro_info = self._parse_distro_release_file( | ||
975 | self.distro_release_file) | ||
976 | basename = os.path.basename(self.distro_release_file) | ||
977 | # The file name pattern for user-specified distro release files | ||
978 | # is somewhat more tolerant (compared to when searching for the | ||
979 | # file), because we want to use what was specified as best as | ||
980 | # possible. | ||
981 | match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) | ||
982 | if match: | ||
983 | distro_info['id'] = match.group(1) | ||
984 | return distro_info | ||
985 | else: | ||
986 | try: | ||
987 | basenames = os.listdir(_UNIXCONFDIR) | ||
988 | # We sort for repeatability in cases where there are multiple | ||
989 | # distro specific files; e.g. CentOS, Oracle, Enterprise all | ||
990 | # containing `redhat-release` on top of their own. | ||
991 | basenames.sort() | ||
992 | except OSError: | ||
993 | # This may occur when /etc is not readable but we can't be | ||
994 | # sure about the *-release files. Check common entries of | ||
995 | # /etc for information. If they turn out to not be there the | ||
996 | # error is handled in `_parse_distro_release_file()`. | ||
997 | basenames = ['SuSE-release', | ||
998 | 'arch-release', | ||
999 | 'base-release', | ||
1000 | 'centos-release', | ||
1001 | 'fedora-release', | ||
1002 | 'gentoo-release', | ||
1003 | 'mageia-release', | ||
1004 | 'mandrake-release', | ||
1005 | 'mandriva-release', | ||
1006 | 'mandrivalinux-release', | ||
1007 | 'manjaro-release', | ||
1008 | 'oracle-release', | ||
1009 | 'redhat-release', | ||
1010 | 'sl-release', | ||
1011 | 'slackware-version'] | ||
1012 | for basename in basenames: | ||
1013 | if basename in _DISTRO_RELEASE_IGNORE_BASENAMES: | ||
1014 | continue | ||
1015 | match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) | ||
1016 | if match: | ||
1017 | filepath = os.path.join(_UNIXCONFDIR, basename) | ||
1018 | distro_info = self._parse_distro_release_file(filepath) | ||
1019 | if 'name' in distro_info: | ||
1020 | # The name is always present if the pattern matches | ||
1021 | self.distro_release_file = filepath | ||
1022 | distro_info['id'] = match.group(1) | ||
1023 | return distro_info | ||
1024 | return {} | ||
1025 | |||
1026 | def _parse_distro_release_file(self, filepath): | ||
1027 | """ | ||
1028 | Parse a distro release file. | ||
1029 | |||
1030 | Parameters: | ||
1031 | |||
1032 | * filepath: Path name of the distro release file. | ||
1033 | |||
1034 | Returns: | ||
1035 | A dictionary containing all information items. | ||
1036 | """ | ||
1037 | try: | ||
1038 | with open(filepath) as fp: | ||
1039 | # Only parse the first line. For instance, on SLES there | ||
1040 | # are multiple lines. We don't want them... | ||
1041 | return self._parse_distro_release_content(fp.readline()) | ||
1042 | except (OSError, IOError): | ||
1043 | # Ignore not being able to read a specific, seemingly version | ||
1044 | # related file. | ||
1045 | # See https://github.com/nir0s/distro/issues/162 | ||
1046 | return {} | ||
1047 | |||
1048 | @staticmethod | ||
1049 | def _parse_distro_release_content(line): | ||
1050 | """ | ||
1051 | Parse a line from a distro release file. | ||
1052 | |||
1053 | Parameters: | ||
1054 | * line: Line from the distro release file. Must be a unicode string | ||
1055 | or a UTF-8 encoded byte string. | ||
1056 | |||
1057 | Returns: | ||
1058 | A dictionary containing all information items. | ||
1059 | """ | ||
1060 | if isinstance(line, bytes): | ||
1061 | line = line.decode('utf-8') | ||
1062 | matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match( | ||
1063 | line.strip()[::-1]) | ||
1064 | distro_info = {} | ||
1065 | if matches: | ||
1066 | # regexp ensures non-None | ||
1067 | distro_info['name'] = matches.group(3)[::-1] | ||
1068 | if matches.group(2): | ||
1069 | distro_info['version_id'] = matches.group(2)[::-1] | ||
1070 | if matches.group(1): | ||
1071 | distro_info['codename'] = matches.group(1)[::-1] | ||
1072 | elif line: | ||
1073 | distro_info['name'] = line.strip() | ||
1074 | return distro_info | ||
1075 | |||
1076 | |||
1077 | _distro = LinuxDistribution() | ||
1078 | |||
1079 | |||
1080 | def main(): | ||
1081 | logger = logging.getLogger(__name__) | ||
1082 | logger.setLevel(logging.DEBUG) | ||
1083 | logger.addHandler(logging.StreamHandler(sys.stdout)) | ||
1084 | |||
1085 | parser = argparse.ArgumentParser(description="Linux distro info tool") | ||
1086 | parser.add_argument( | ||
1087 | '--json', | ||
1088 | '-j', | ||
1089 | help="Output in machine readable format", | ||
1090 | action="store_true") | ||
1091 | args = parser.parse_args() | ||
1092 | |||
1093 | if args.json: | ||
1094 | logger.info(json.dumps(info(), indent=4, sort_keys=True)) | ||
1095 | else: | ||
1096 | logger.info('Name: %s', name(pretty=True)) | ||
1097 | distribution_version = version(pretty=True) | ||
1098 | logger.info('Version: %s', distribution_version) | ||
1099 | distribution_codename = codename() | ||
1100 | logger.info('Codename: %s', distribution_codename) | ||
1101 | |||
1102 | |||
1103 | if __name__ == '__main__': | ||
1104 | main() | ||