From adc69f350550ff3d4bbad8a2a8a2e1414b479a74 Mon Sep 17 00:00:00 2001 From: oaken-source Date: Fri, 22 Nov 2019 06:49:26 +0100 Subject: libre/iceweasel: updated to 70.0.1 --- libre/iceweasel/firefox-70.0-add-distro.patch | 6965 +++++++++++++++++++++++++ 1 file changed, 6965 insertions(+) create mode 100644 libre/iceweasel/firefox-70.0-add-distro.patch (limited to 'libre/iceweasel/firefox-70.0-add-distro.patch') diff --git a/libre/iceweasel/firefox-70.0-add-distro.patch b/libre/iceweasel/firefox-70.0-add-distro.patch new file mode 100644 index 000000000..8db8ce5dc --- /dev/null +++ b/libre/iceweasel/firefox-70.0-add-distro.patch @@ -0,0 +1,6965 @@ + +# HG changeset patch +# User Edwin Takahashi +# Date 1572468170 0 +# Node ID d2d9fe01fc33af4538940e833a6696d973ebea74 +# Parent eddb9fcaaa4bd4fdb4e32024f92f969abfe92f58 +Bug 1212502 - Switch mozinfo to using the 'distro' package to get linux distribution info r=ahal,KWierso + +Differential Revision: https://phabricator.services.mozilla.com/D49366 + +diff --git a/build/virtualenv_packages.txt b/build/virtualenv_packages.txt +--- a/build/virtualenv_packages.txt ++++ b/build/virtualenv_packages.txt +@@ -10,16 +10,17 @@ mozilla.pth:third_party/python/atomicwri + mozilla.pth:third_party/python/attrs/src + python2:mozilla.pth:third_party/python/backports + mozilla.pth:third_party/python/biplist + mozilla.pth:third_party/python/blessings + mozilla.pth:third_party/python/Click + mozilla.pth:third_party/python/compare-locales + mozilla.pth:third_party/python/configobj + mozilla.pth:third_party/python/cram ++mozilla.pth:third_party/python/distro + mozilla.pth:third_party/python/dlmanager + mozilla.pth:third_party/python/enum34 + mozilla.pth:third_party/python/fluent + mozilla.pth:third_party/python/funcsigs + python2:mozilla.pth:third_party/python/futures + mozilla.pth:third_party/python/mohawk + mozilla.pth:third_party/python/more-itertools + mozilla.pth:third_party/python/mozilla-version +diff --git a/python/mozbuild/mozbuild/action/test_archive.py b/python/mozbuild/mozbuild/action/test_archive.py +--- a/python/mozbuild/mozbuild/action/test_archive.py ++++ b/python/mozbuild/mozbuild/action/test_archive.py +@@ -245,16 +245,22 @@ ARCHIVE_FILES = { + }, + { + 'source': buildconfig.topsrcdir, + 'base': 'third_party/python/six', + 'pattern': '**', + 'dest': 'tools/six', + }, + { ++ 'source': buildconfig.topsrcdir, ++ 'base': 'third_party/python/distro', ++ 'pattern': '**', ++ 'dest': 'tools/distro', ++ }, ++ { + 'source': buildconfig.topobjdir, + 'base': '', + 'pattern': 'mozinfo.json', + }, + { + 'source': buildconfig.topobjdir, + 'base': 'dist/bin', + 'patterns': [ +@@ -431,16 +437,22 @@ ARCHIVE_FILES = { + 'dest': 'mozharness', + }, + { + 'source': buildconfig.topsrcdir, + 'base': 'third_party/python/six', + 'pattern': 'six.py', + 'dest': 'mozharness', + }, ++ { ++ 'source': buildconfig.topsrcdir, ++ 'base': 'third_party/python/distro', ++ 'pattern': 'distro.py', ++ 'dest': 'mozharness', ++ }, + ], + 'reftest': [ + { + 'source': buildconfig.topobjdir, + 'base': '_tests', + 'pattern': 'reftest/**', + }, + { +diff --git a/testing/mozbase/mozinfo/mozinfo/mozinfo.py b/testing/mozbase/mozinfo/mozinfo/mozinfo.py +--- a/testing/mozbase/mozinfo/mozinfo/mozinfo.py ++++ b/testing/mozbase/mozinfo/mozinfo/mozinfo.py +@@ -9,16 +9,17 @@ + # information and having behaviour depend on it + + from __future__ import absolute_import, print_function + + import os + import platform + import re + import sys ++ + from .string_version import StringVersion + from ctypes.util import find_library + + # keep a copy of the os module since updating globals overrides this + _os = os + + + class unknown(object): +@@ -93,35 +94,45 @@ if system in ["Microsoft", "Windows"]: + version = "%d.%d.%d" % (major, minor, build_number) + + os_version = "%d.%d" % (major, minor) + elif system.startswith(('MINGW', 'MSYS_NT')): + # windows/mingw python build (msys) + info['os'] = 'win' + os_version = version = unknown + elif system == "Linux": +- if hasattr(platform, "linux_distribution"): +- (distro, os_version, codename) = platform.linux_distribution() ++ # Only attempt to import distro for Linux. ++ # https://github.com/nir0s/distro/issues/177 ++ try: ++ import distro ++ except ImportError: ++ pass ++ # First use distro package, then fall back to platform. ++ # This will only until Mozilla upgrades python to 3.8. ++ if hasattr(distro, "linux_distribution"): ++ (distribution, os_version, codename) = distro.linux_distribution() ++ elif hasattr(platform, "linux_distribution"): ++ (distribution, os_version, codename) = platform.linux_distribution() + else: +- (distro, os_version, codename) = platform.dist() ++ (distribution, os_version, codename) = platform.dist() + if not processor: + processor = machine +- version = "%s %s" % (distro, os_version) ++ version = "%s %s" % (distribution, os_version) + + # Bug in Python 2's `platform` library: + # It will return a triple of empty strings if the distribution is not supported. + # It works on Python 3. If we don't have an OS version, + # the unit tests fail to run. +- if not distro and not os_version and not codename: +- distro = 'lfs' ++ if not distribution and not os_version and not codename: ++ distribution = 'lfs' + version = release + os_version = release + + info['os'] = 'linux' +- info['linux_distro'] = distro ++ info['linux_distro'] = distribution + elif system in ['DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD']: + info['os'] = 'bsd' + version = os_version = sys.platform + elif system == "Darwin": + (release, versioninfo, machine) = platform.mac_ver() + version = "OS X %s" % release + versionNums = release.split('.')[:2] + os_version = "%s.%s" % (versionNums[0], versionNums[1]) +diff --git a/testing/mozbase/mozinfo/setup.py b/testing/mozbase/mozinfo/setup.py +--- a/testing/mozbase/mozinfo/setup.py ++++ b/testing/mozbase/mozinfo/setup.py +@@ -4,17 +4,20 @@ + + from __future__ import absolute_import + + from setuptools import setup + + PACKAGE_VERSION = "1.1.0" + + # dependencies +-deps = ["mozfile >= 0.12"] ++deps = [ ++ "distro == 1.4.0", ++ "mozfile >= 0.12", ++] + + setup( + name="mozinfo", + version=PACKAGE_VERSION, + description="Library to get system information for use in Mozilla testing", + long_description="see https://firefox-source-docs.mozilla.org/mozbase/index.html", + classifiers=[ + "Programming Language :: Python :: 2.7", +diff --git a/testing/mozharness/tox.ini b/testing/mozharness/tox.ini +--- a/testing/mozharness/tox.ini ++++ b/testing/mozharness/tox.ini +@@ -1,14 +1,15 @@ + [tox] + envlist = py27-hg4.3 + + [base] + deps = + coverage ++ distro + nose + rednose + {toxinidir}/../mozbase/mozlog + mozbase = {toxinidir}/../mozbase/ + + + [testenv] + basepython = python2.7 +diff --git a/third_party/python/distro/CHANGELOG.md b/third_party/python/distro/CHANGELOG.md +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/CHANGELOG.md +@@ -0,0 +1,147 @@ ++## 1.4.0 (2019.2.4) ++ ++BACKWARD COMPATIBILITY: ++* Prefer the VERSION_CODENAME field of os-release to parsing it from VERSION [[#230](https://github.com/nir0s/distro/pull/230)] ++ ++BUG FIXES: ++* Return _uname_info from the uname_info() method [[#233](https://github.com/nir0s/distro/pull/233)] ++* Fixed CloudLinux id discovery [[#234](https://github.com/nir0s/distro/pull/234)] ++* Update Oracle matching [[#224](https://github.com/nir0s/distro/pull/224)] ++ ++DOCS: ++* Update Fedora package link [[#225](https://github.com/nir0s/distro/pull/225)] ++* Distro is the recommended replacement for platform.linux_distribution [[#220](https://github.com/nir0s/distro/pull/220)] ++ ++RELEASE: ++* Use Markdown for long description in setup.py [[#219](https://github.com/nir0s/distro/pull/219)] ++ ++Additionally, The Python2.6 branch was fixed and rebased on top of master. It is now passing all tests. Thanks [abadger](https://github.com/abadger)! ++ ++## 1.3.0 (2018.05.09) ++ ++ENHANCEMENTS: ++* Added support for OpenBSD, FreeBSD, and NetBSD [[#207](https://github.com/nir0s/distro/issues/207)] ++ ++TESTS: ++* Add test for Kali Linux Rolling [[#214](https://github.com/nir0s/distro/issues/214)] ++ ++DOCS: ++* Update docs with regards to #207 [[#209](https://github.com/nir0s/distro/issues/209)] ++* Add Ansible reference implementation and fix arch-linux link [[#213](https://github.com/nir0s/distro/issues/213)] ++* Add facter reference implementation [[#213](https://github.com/nir0s/distro/issues/213)] ++ ++## 1.2.0 (2017.12.24) ++ ++BACKWARD COMPATIBILITY: ++* Don't raise ImportError on non-linux platforms [[#202](https://github.com/nir0s/distro/issues/202)] ++ ++ENHANCEMENTS: ++* Lazily load the LinuxDistribution data [[#201](https://github.com/nir0s/distro/issues/201)] ++ ++BUG FIXES: ++* Stdout of shell should be decoded with sys.getfilesystemencoding() [[#203](https://github.com/nir0s/distro/issues/203)] ++ ++TESTS: ++* Explicitly set Python versions on Travis for flake [[#204](https://github.com/nir0s/distro/issues/204)] ++ ++ ++## 1.1.0 (2017.11.28) ++ ++BACKWARD COMPATIBILITY: ++* Drop python3.3 support [[#199](https://github.com/nir0s/distro/issues/199)] ++* Remove Official Python26 support [[#195](https://github.com/nir0s/distro/issues/195)] ++ ++TESTS: ++* Add MandrivaLinux test case [[#181](https://github.com/nir0s/distro/issues/181)] ++* Add test cases for CloudLinux 5, 6, and 7 [[#180](https://github.com/nir0s/distro/issues/180)] ++ ++RELEASE: ++* Modify MANIFEST to include resources for tests and docs in source tarballs [[97c91a1](97c91a1)] ++ ++## 1.0.4 (2017.04.01) ++ ++BUG FIXES: ++* Guess common *-release files if /etc not readable [[#175](https://github.com/nir0s/distro/issues/175)] ++ ++## 1.0.3 (2017.03.19) ++ ++ENHANCEMENTS: ++* Show keys for empty values when running distro from the CLI [[#160](https://github.com/nir0s/distro/issues/160)] ++* Add manual mapping for `redhatenterpriseserver` (previously only redhatenterpriseworkstation was mapped) [[#148](https://github.com/nir0s/distro/issues/148)] ++* Race condition in `_parse_distro_release_file` [[#163](https://github.com/nir0s/distro/issues/163)] ++ ++TESTS: ++* Add RHEL5 test case [[#165](https://github.com/nir0s/distro/issues/165)] ++* Add OpenELEC test case [[#166](https://github.com/nir0s/distro/issues/166)] ++* Replace nose with pytest [[#158](https://github.com/nir0s/distro/issues/158)] ++ ++RELEASE: ++* Update classifiers ++* Update supported Python versions (with py36) ++ ++## 1.0.2 (2017.01.12) ++ ++TESTS: ++* Test on py33, py36 and py3 based flake8 ++ ++RELEASE: ++* Add MANIFEST file (which also includes the LICENSE as part of Issue [[#139](https://github.com/nir0s/distro/issues/139)]) ++* Default to releasing using Twine [[#121](https://github.com/nir0s/distro/issues/121)] ++* Add setup.cfg file [[#145](https://github.com/nir0s/distro/issues/145)] ++* Update license in setup.py ++ ++## 1.0.1 (2016-11-03) ++ ++ENHANCEMENTS: ++* Prettify distro -j's output and add more elaborate docs [[#147](https://github.com/nir0s/distro/issues/147)] ++* Decode output of `lsb_release` as utf-8 [[#144](https://github.com/nir0s/distro/issues/144)] ++* Logger now uses `message %s, string` form to not-evaulate log messages if unnecessary [[#145](https://github.com/nir0s/distro/issues/145)] ++ ++TESTS: ++* Increase code-coverage [[#146](https://github.com/nir0s/distro/issues/146)] ++* Fix landscape code-quality warnings [[#145](https://github.com/nir0s/distro/issues/145)] ++ ++RELEASE: ++* Add CONTRIBUTING.md ++ ++## 1.0.0 (2016-09-25) ++ ++BACKWARD COMPATIBILITY: ++* raise exception when importing on non-supported platforms [[#129](https://github.com/nir0s/distro/issues/129)] ++ ++ENHANCEMENTS: ++* Use `bytes` invariantly [[#135](https://github.com/nir0s/distro/issues/135)] ++* Some minor code adjustments plus a CLI [[#134](https://github.com/nir0s/distro/issues/134)] ++* Emit stderr if `lsb_release` fails ++ ++BUG FIXES: ++* Fix some encoding related issues ++ ++TESTS: ++* Add many test cases (e.g. Raspbian 8, CoreOS, Amazon Linux, Scientific Linux, Gentoo, Manjaro) ++* Completely redo the testing framework to make it easier to add tests ++* Test on pypy ++ ++RELEASE: ++* Remove six as a dependency ++ ++## 0.6.0 (2016-04-21) ++ ++This is the first release of `distro`. ++All previous work was done on `ld` and therefore unmentioned here. See the release log in GitHub if you want the entire log. ++ ++BACKWARD COMPATIBILITY: ++* No longer a package. constants.py has been removed and distro is now a single module ++ ++ENHANCEMENTS: ++* distro.info() now receives best and pretty flags ++* Removed get_ prefix from get_*_release_attr functions ++* Codename is now passed in distro.info() ++ ++TESTS: ++* Added Linux Mint test case ++* Now testing on Python 3.4 ++ ++DOCS: ++* Documentation fixes ++ +diff --git a/third_party/python/distro/CONTRIBUTING.md b/third_party/python/distro/CONTRIBUTING.md +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/CONTRIBUTING.md +@@ -0,0 +1,54 @@ ++# General ++ ++* Contributing to distro identification currently doesn't have any specific standards and rather depends on the specific implementation. ++* A 100% coverage is expected for each PR unless explicitly authorized by the reviewer. ++* Please try to maintain maximum code-health (via landscape.io). ++ ++# Contributing distro specific tests ++ ++Distro's tests are implemented via a standardized framework under `tests/test_distro.py` ++ ++For each distribution, tests should be added in the relevant class according to which distribution file(s) exists on it, so, for example, tests should be added under `TestOSRelease` where `/etc/os-release` is available. ++ ++The tests must be self-contained, meaning that the release files for the distribution should be maintained in the repository under `tests/resources/distros/distribution_name+distribution_version`. ++ ++A tests method would like somewhat like this: ++ ++```python ++def test_centos7_os_release(self): ++ desired_outcome = { ++ 'id': 'centos', ++ 'name': 'CentOS Linux', ++ 'pretty_name': 'CentOS Linux 7 (Core)', ++ 'version': '7', ++ 'pretty_version': '7 (Core)', ++ 'best_version': '7', ++ 'like': 'rhel fedora', ++ 'codename': 'Core' ++ } ++ self._test_outcome(desired_outcome) ++``` ++ ++The framework will automatically try to pick up the relevant file according to the method's name (`centos7` meaning the folder should be named `centos7` as well) and compare the `desired_outcome` with the parsed files found under the test dir. ++ ++The exception to the rule is under the `TestDistroRelease` test class which should look somewhat like this: ++ ++```python ++def test_centos5_dist_release(self): ++ desired_outcome = { ++ 'id': 'centos', ++ 'name': 'CentOS', ++ 'pretty_name': 'CentOS 5.11 (Final)', ++ 'version': '5.11', ++ 'pretty_version': '5.11 (Final)', ++ 'best_version': '5.11', ++ 'codename': 'Final', ++ 'major_version': '5', ++ 'minor_version': '11' ++ } ++ self._test_outcome(desired_outcome, 'centos', '5') ++``` ++ ++Where the name of the method is not indicative of the lookup folder but rather tha two last arguments in `_test_outcome`. ++ ++A test case is mandatory under `TestOverall` for a PR to be complete. +\ No newline at end of file +diff --git a/third_party/python/distro/CONTRIBUTORS.md b/third_party/python/distro/CONTRIBUTORS.md +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/CONTRIBUTORS.md +@@ -0,0 +1,13 @@ ++Thanks! ++ ++* https://github.com/andy-maier ++* https://github.com/SethMichaelLarson ++* https://github.com/asottile ++* https://github.com/MartijnBraam ++* https://github.com/funkyfuture ++* https://github.com/adamjstewart ++* https://github.com/xavfernandez ++* https://github.com/xsuchy ++* https://github.com/marcoceppi ++* https://github.com/tgamblin ++* https://github.com/sebix +diff --git a/third_party/python/distro/LICENSE b/third_party/python/distro/LICENSE +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/LICENSE +@@ -0,0 +1,202 @@ ++Apache License ++ Version 2.0, January 2004 ++ http://www.apache.org/licenses/ ++ ++ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION ++ ++ 1. Definitions. ++ ++ "License" shall mean the terms and conditions for use, reproduction, ++ and distribution as defined by Sections 1 through 9 of this document. ++ ++ "Licensor" shall mean the copyright owner or entity authorized by ++ the copyright owner that is granting the License. ++ ++ "Legal Entity" shall mean the union of the acting entity and all ++ other entities that control, are controlled by, or are under common ++ control with that entity. For the purposes of this definition, ++ "control" means (i) the power, direct or indirect, to cause the ++ direction or management of such entity, whether by contract or ++ otherwise, or (ii) ownership of fifty percent (50%) or more of the ++ outstanding shares, or (iii) beneficial ownership of such entity. ++ ++ "You" (or "Your") shall mean an individual or Legal Entity ++ exercising permissions granted by this License. ++ ++ "Source" form shall mean the preferred form for making modifications, ++ including but not limited to software source code, documentation ++ source, and configuration files. ++ ++ "Object" form shall mean any form resulting from mechanical ++ transformation or translation of a Source form, including but ++ not limited to compiled object code, generated documentation, ++ and conversions to other media types. ++ ++ "Work" shall mean the work of authorship, whether in Source or ++ Object form, made available under the License, as indicated by a ++ copyright notice that is included in or attached to the work ++ (an example is provided in the Appendix below). ++ ++ "Derivative Works" shall mean any work, whether in Source or Object ++ form, that is based on (or derived from) the Work and for which the ++ editorial revisions, annotations, elaborations, or other modifications ++ represent, as a whole, an original work of authorship. For the purposes ++ of this License, Derivative Works shall not include works that remain ++ separable from, or merely link (or bind by name) to the interfaces of, ++ the Work and Derivative Works thereof. ++ ++ "Contribution" shall mean any work of authorship, including ++ the original version of the Work and any modifications or additions ++ to that Work or Derivative Works thereof, that is intentionally ++ submitted to Licensor for inclusion in the Work by the copyright owner ++ or by an individual or Legal Entity authorized to submit on behalf of ++ the copyright owner. For the purposes of this definition, "submitted" ++ means any form of electronic, verbal, or written communication sent ++ to the Licensor or its representatives, including but not limited to ++ communication on electronic mailing lists, source code control systems, ++ and issue tracking systems that are managed by, or on behalf of, the ++ Licensor for the purpose of discussing and improving the Work, but ++ excluding communication that is conspicuously marked or otherwise ++ designated in writing by the copyright owner as "Not a Contribution." ++ ++ "Contributor" shall mean Licensor and any individual or Legal Entity ++ on behalf of whom a Contribution has been received by Licensor and ++ subsequently incorporated within the Work. ++ ++ 2. Grant of Copyright License. Subject to the terms and conditions of ++ this License, each Contributor hereby grants to You a perpetual, ++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable ++ copyright license to reproduce, prepare Derivative Works of, ++ publicly display, publicly perform, sublicense, and distribute the ++ Work and such Derivative Works in Source or Object form. ++ ++ 3. Grant of Patent License. Subject to the terms and conditions of ++ this License, each Contributor hereby grants to You a perpetual, ++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable ++ (except as stated in this section) patent license to make, have made, ++ use, offer to sell, sell, import, and otherwise transfer the Work, ++ where such license applies only to those patent claims licensable ++ by such Contributor that are necessarily infringed by their ++ Contribution(s) alone or by combination of their Contribution(s) ++ with the Work to which such Contribution(s) was submitted. If You ++ institute patent litigation against any entity (including a ++ cross-claim or counterclaim in a lawsuit) alleging that the Work ++ or a Contribution incorporated within the Work constitutes direct ++ or contributory patent infringement, then any patent licenses ++ granted to You under this License for that Work shall terminate ++ as of the date such litigation is filed. ++ ++ 4. Redistribution. You may reproduce and distribute copies of the ++ Work or Derivative Works thereof in any medium, with or without ++ modifications, and in Source or Object form, provided that You ++ meet the following conditions: ++ ++ (a) You must give any other recipients of the Work or ++ Derivative Works a copy of this License; and ++ ++ (b) You must cause any modified files to carry prominent notices ++ stating that You changed the files; and ++ ++ (c) You must retain, in the Source form of any Derivative Works ++ that You distribute, all copyright, patent, trademark, and ++ attribution notices from the Source form of the Work, ++ excluding those notices that do not pertain to any part of ++ the Derivative Works; and ++ ++ (d) If the Work includes a "NOTICE" text file as part of its ++ distribution, then any Derivative Works that You distribute must ++ include a readable copy of the attribution notices contained ++ within such NOTICE file, excluding those notices that do not ++ pertain to any part of the Derivative Works, in at least one ++ of the following places: within a NOTICE text file distributed ++ as part of the Derivative Works; within the Source form or ++ documentation, if provided along with the Derivative Works; or, ++ within a display generated by the Derivative Works, if and ++ wherever such third-party notices normally appear. The contents ++ of the NOTICE file are for informational purposes only and ++ do not modify the License. You may add Your own attribution ++ notices within Derivative Works that You distribute, alongside ++ or as an addendum to the NOTICE text from the Work, provided ++ that such additional attribution notices cannot be construed ++ as modifying the License. ++ ++ You may add Your own copyright statement to Your modifications and ++ may provide additional or different license terms and conditions ++ for use, reproduction, or distribution of Your modifications, or ++ for any such Derivative Works as a whole, provided Your use, ++ reproduction, and distribution of the Work otherwise complies with ++ the conditions stated in this License. ++ ++ 5. Submission of Contributions. Unless You explicitly state otherwise, ++ any Contribution intentionally submitted for inclusion in the Work ++ by You to the Licensor shall be under the terms and conditions of ++ this License, without any additional terms or conditions. ++ Notwithstanding the above, nothing herein shall supersede or modify ++ the terms of any separate license agreement you may have executed ++ with Licensor regarding such Contributions. ++ ++ 6. Trademarks. This License does not grant permission to use the trade ++ names, trademarks, service marks, or product names of the Licensor, ++ except as required for reasonable and customary use in describing the ++ origin of the Work and reproducing the content of the NOTICE file. ++ ++ 7. Disclaimer of Warranty. Unless required by applicable law or ++ agreed to in writing, Licensor provides the Work (and each ++ Contributor provides its Contributions) on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or ++ implied, including, without limitation, any warranties or conditions ++ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A ++ PARTICULAR PURPOSE. You are solely responsible for determining the ++ appropriateness of using or redistributing the Work and assume any ++ risks associated with Your exercise of permissions under this License. ++ ++ 8. Limitation of Liability. In no event and under no legal theory, ++ whether in tort (including negligence), contract, or otherwise, ++ unless required by applicable law (such as deliberate and grossly ++ negligent acts) or agreed to in writing, shall any Contributor be ++ liable to You for damages, including any direct, indirect, special, ++ incidental, or consequential damages of any character arising as a ++ result of this License or out of the use or inability to use the ++ Work (including but not limited to damages for loss of goodwill, ++ work stoppage, computer failure or malfunction, or any and all ++ other commercial damages or losses), even if such Contributor ++ has been advised of the possibility of such damages. ++ ++ 9. Accepting Warranty or Additional Liability. While redistributing ++ the Work or Derivative Works thereof, You may choose to offer, ++ and charge a fee for, acceptance of support, warranty, indemnity, ++ or other liability obligations and/or rights consistent with this ++ License. However, in accepting such obligations, You may act only ++ on Your own behalf and on Your sole responsibility, not on behalf ++ of any other Contributor, and only if You agree to indemnify, ++ defend, and hold each Contributor harmless for any liability ++ incurred by, or claims asserted against, such Contributor by reason ++ of your accepting any such warranty or additional liability. ++ ++ END OF TERMS AND CONDITIONS ++ ++ APPENDIX: How to apply the Apache License to your work. ++ ++ To apply the Apache License to your work, attach the following ++ boilerplate notice, with the fields enclosed by brackets "{}" ++ replaced with your own identifying information. (Don't include ++ the brackets!) The text should be enclosed in the appropriate ++ comment syntax for the file format. We also recommend that a ++ file or class name and description of purpose be included on the ++ same "printed page" as the copyright notice for easier ++ identification within third-party archives. ++ ++ Copyright {yyyy} {name of copyright owner} ++ ++ Licensed under the Apache License, Version 2.0 (the "License"); ++ you may not use this file except in compliance with the License. ++ You may obtain a copy of the License at ++ ++ http://www.apache.org/licenses/LICENSE-2.0 ++ ++ Unless required by applicable law or agreed to in writing, software ++ distributed under the License is distributed on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ See the License for the specific language governing permissions and ++ limitations under the License. ++ +diff --git a/third_party/python/distro/MANIFEST.in b/third_party/python/distro/MANIFEST.in +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/MANIFEST.in +@@ -0,0 +1,12 @@ ++include *.md ++include *.py ++include *.txt ++include LICENSE ++include CHANGES ++include Makefile ++ ++graft tests ++ ++include docs/* ++ ++global-exclude *.py[co] +diff --git a/third_party/python/distro/Makefile b/third_party/python/distro/Makefile +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/Makefile +@@ -0,0 +1,145 @@ ++# Copyright 2015,2016 Nir Cohen ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++# Name of this package ++PACKAGENAME = distro ++ ++# Additional options for Sphinx ++SPHINXOPTS = -v ++ ++# Paper format for the Sphinx LaTex/PDF builder. ++# Valid values: a4, letter ++SPHINXPAPER = a4 ++ ++# Sphinx build subtree. ++SPHINXBUILDDIR = build_docs ++ ++# Directory where conf.py is located ++SPHINXCONFDIR = docs ++ ++# Directory where input files for Sphinx are located ++SPHINXSOURCEDIR = . ++ ++# Sphinx build command (Use 'pip install sphinx' to get it) ++SPHINXBUILD = sphinx-build ++ ++# Internal variables for Sphinx ++SPHINXPAPEROPT_a4 = -D latex_paper_size=a4 ++SPHINXPAPEROPT_letter = -D latex_paper_size=letter ++ALLSPHINXOPTS = -d $(SPHINXBUILDDIR)/doctrees -c $(SPHINXCONFDIR) \ ++ $(SPHINXPAPEROPT_$(SPHINXPAPER)) $(SPHINXOPTS) \ ++ $(SPHINXSOURCEDIR) ++ ++.PHONY: help ++help: ++ @echo 'Please use "make " where is one of' ++ @echo " release - build a release and publish it" ++ @echo " dev - prepare a development environment (includes tests)" ++ @echo " instdev - prepare a development environment (no tests)" ++ @echo " install - install into current Python environment" ++ @echo " html - generate docs as standalone HTML files in: $(SPHINXBUILDDIR)/html" ++ @echo " pdf - generate docs as PDF (via LaTeX) for paper format: $(SPHINXPAPER) in: $(SPHINXBUILDDIR)/pdf" ++ @echo " man - generate docs as manual pages in: $(SPHINXBUILDDIR)/man" ++ @echo " docchanges - generate an overview of all changed/added/deprecated items in docs" ++ @echo " doclinkcheck - check all external links in docs for integrity" ++ @echo " doccoverage - run coverage check of the documentation" ++ @echo " clobber - remove any build products" ++ @echo " build - build the package" ++ @echo " test - test from this directory using tox, including test coverage" ++ @echo " publish - upload to PyPI" ++ @echo " clean - remove any temporary build products" ++ @echo " dry-run - perform all action required for a release without actually releasing" ++ ++.PHONY: release ++release: test clean build publish ++ @echo "$@ done." ++ ++.PHONY: test ++test: ++ pip install 'tox>=1.7.2' ++ tox ++ @echo "$@ done." ++ ++.PHONY: clean ++clean: ++ rm -rf dist build $(PACKAGENAME).egg-info ++ @echo "$@ done." ++ ++.PHONY: build ++build: ++ python setup.py sdist bdist_wheel ++ ++.PHONY: publish ++publish: ++ twine upload -r pypi dist/$(PACKAGENAME)-* ++ @echo "$@ done." ++ ++.PHONY: dry-run ++dry-run: test clean build ++ @echo "$@ done." ++ ++.PHONY: dev ++dev: instdev test ++ @echo "$@ done." ++ ++.PHONY: instdev ++instdev: ++ pip install -r dev-requirements.txt ++ python setup.py develop ++ @echo "$@ done." ++ ++.PHONY: install ++install: ++ python setup.py install ++ @echo "$@ done." ++ ++.PHONY: html ++html: ++ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/html ++ @echo "$@ done; the HTML pages are in $(SPHINXBUILDDIR)/html." ++ ++.PHONY: pdf ++pdf: ++ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/pdf ++ @echo "Running LaTeX files through pdflatex..." ++ $(MAKE) -C $(SPHINXBUILDDIR)/pdf all-pdf ++ @echo "$@ done; the PDF files are in $(SPHINXBUILDDIR)/pdf." ++ ++.PHONY: man ++man: ++ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/man ++ @echo "$@ done; the manual pages are in $(SPHINXBUILDDIR)/man." ++ ++.PHONY: docchanges ++docchanges: ++ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/changes ++ @echo ++ @echo "$@ done; the doc changes overview file is in $(SPHINXBUILDDIR)/changes." ++ ++.PHONY: doclinkcheck ++doclinkcheck: ++ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/linkcheck ++ @echo ++ @echo "$@ done; look for any errors in the above output " \ ++ "or in $(SPHINXBUILDDIR)/linkcheck/output.txt." ++ ++.PHONY: doccoverage ++doccoverage: ++ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/coverage ++ @echo "$@ done; the doc coverage results are in $(SPHINXBUILDDIR)/coverage/python.txt." ++ ++.PHONY: clobber ++clobber: clean ++ rm -rf $(SPHINXBUILDDIR) ++ @echo "$@ done." +diff --git a/third_party/python/distro/PKG-INFO b/third_party/python/distro/PKG-INFO +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/PKG-INFO +@@ -0,0 +1,168 @@ ++Metadata-Version: 2.1 ++Name: distro ++Version: 1.4.0 ++Summary: Distro - an OS platform information API ++Home-page: https://github.com/nir0s/distro ++Author: Nir Cohen ++Author-email: nir36g@gmail.com ++License: Apache License, Version 2.0 ++Description: Distro - an OS platform information API ++ ======================================= ++ ++ [![Build Status](https://travis-ci.org/nir0s/distro.svg?branch=master)](https://travis-ci.org/nir0s/distro) ++ [![Build status](https://ci.appveyor.com/api/projects/status/e812qjk1gf0f74r5/branch/master?svg=true)](https://ci.appveyor.com/project/nir0s/distro/branch/master) ++ [![PyPI version](http://img.shields.io/pypi/v/distro.svg)](https://pypi.python.org/pypi/distro) ++ [![Supported Python Versions](https://img.shields.io/pypi/pyversions/distro.svg)](https://img.shields.io/pypi/pyversions/distro.svg) ++ [![Requirements Status](https://requires.io/github/nir0s/distro/requirements.svg?branch=master)](https://requires.io/github/nir0s/distro/requirements/?branch=master) ++ [![Code Coverage](https://codecov.io/github/nir0s/distro/coverage.svg?branch=master)](https://codecov.io/github/nir0s/distro?branch=master) ++ [![Code Quality](https://landscape.io/github/nir0s/distro/master/landscape.svg?style=flat)](https://landscape.io/github/nir0s/distro) ++ [![Is Wheel](https://img.shields.io/pypi/wheel/distro.svg?style=flat)](https://pypi.python.org/pypi/distro) ++ [![Latest Github Release](https://readthedocs.org/projects/distro/badge/?version=stable)](http://distro.readthedocs.io/en/latest/) ++ [![Join the chat at https://gitter.im/nir0s/distro](https://badges.gitter.im/nir0s/distro.svg)](https://gitter.im/nir0s/distro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ++ ++ `distro` provides information about the ++ OS distribution it runs on, such as a reliable machine-readable ID, or ++ version information. ++ ++ It is the recommended replacement for Python's original ++ [`platform.linux_distribution`](https://docs.python.org/3.7/library/platform.html#platform.linux_distribution) ++ function (which will be removed in Python 3.8). ++ It also provides much more functionality which isn't necessarily Python bound, ++ like a command-line interface. ++ ++ Distro currently supports Linux and BSD based systems but [Windows and OS X support](https://github.com/nir0s/distro/issues/177) is also planned. ++ ++ For Python 2.6 support, see https://github.com/nir0s/distro/tree/python2.6-support ++ ++ ## Installation ++ ++ Installation of the latest released version from PyPI: ++ ++ ```shell ++ pip install distro ++ ``` ++ ++ Installation of the latest development version: ++ ++ ```shell ++ pip install https://github.com/nir0s/distro/archive/master.tar.gz ++ ``` ++ ++ ++ ## Usage ++ ++ ```bash ++ $ distro ++ Name: Antergos Linux ++ Version: 2015.10 (ISO-Rolling) ++ Codename: ISO-Rolling ++ ++ $ distro -j ++ { ++ "codename": "ISO-Rolling", ++ "id": "antergos", ++ "like": "arch", ++ "version": "16.9", ++ "version_parts": { ++ "build_number": "", ++ "major": "16", ++ "minor": "9" ++ } ++ } ++ ++ ++ $ python ++ >>> import distro ++ >>> distro.linux_distribution(full_distribution_name=False) ++ ('centos', '7.1.1503', 'Core') ++ ``` ++ ++ ++ ## Documentation ++ ++ On top of the aforementioned API, several more functions are available. For a complete description of the ++ API, see the [latest API documentation](http://distro.readthedocs.org/en/latest/). ++ ++ ## Background ++ ++ An alternative implementation became necessary because Python 3.5 deprecated ++ this function, and Python 3.8 will remove it altogether. ++ Its predecessor function `platform.dist` was already deprecated since ++ Python 2.6 and will also be removed in Python 3.8. ++ Still, there are many cases in which access to that information is needed. ++ See [Python issue 1322](https://bugs.python.org/issue1322) for more ++ information. ++ ++ The `distro` package implements a robust and inclusive way of retrieving the ++ information about a distribution based on new standards and old methods, ++ namely from these data sources (from high to low precedence): ++ ++ * The os-release file `/etc/os-release`, if present. ++ * The output of the `lsb_release` command, if available. ++ * The distro release file (`/etc/*(-|_)(release|version)`), if present. ++ * The `uname` command for BSD based distrubtions. ++ ++ ++ ## Python and Distribution Support ++ ++ `distro` is supported and tested on Python 2.7, 3.4+ and PyPy and on ++ any distribution that provides one or more of the data sources ++ covered. ++ ++ This package is tested with test data that mimics the exact behavior of the data sources of [a number of Linux distributions](https://github.com/nir0s/distro/tree/master/tests/resources/distros). ++ ++ ++ ## Testing ++ ++ ```shell ++ git clone git@github.com:nir0s/distro.git ++ cd distro ++ pip install tox ++ tox ++ ``` ++ ++ ++ ## Contributions ++ ++ Pull requests are always welcome to deal with specific distributions or just ++ for general merriment. ++ ++ See [CONTRIBUTIONS](https://github.com/nir0s/distro/blob/master/CONTRIBUTING.md) for contribution info. ++ ++ Reference implementations for supporting additional distributions and file ++ formats can be found here: ++ ++ * https://github.com/saltstack/salt/blob/develop/salt/grains/core.py#L1172 ++ * https://github.com/chef/ohai/blob/master/lib/ohai/plugins/linux/platform.rb ++ * https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/facts/system/distribution.py ++ * https://github.com/puppetlabs/facter/blob/master/lib/src/facts/linux/os_linux.cc ++ ++ ## Package manager distributions ++ ++ * https://src.fedoraproject.org/rpms/python-distro ++ * https://www.archlinux.org/packages/community/any/python-distro/ ++ * https://launchpad.net/ubuntu/+source/python-distro ++ * https://packages.debian.org/sid/python-distro ++ * https://packages.gentoo.org/packages/dev-python/distro ++ * https://pkgs.org/download/python2-distro ++ * https://slackbuilds.org/repository/14.2/python/python-distro/ ++ ++Platform: All ++Classifier: Development Status :: 5 - Production/Stable ++Classifier: Intended Audience :: Developers ++Classifier: Intended Audience :: System Administrators ++Classifier: License :: OSI Approved :: Apache Software License ++Classifier: Operating System :: POSIX :: Linux ++Classifier: Operating System :: POSIX :: BSD ++Classifier: Operating System :: POSIX :: BSD :: FreeBSD ++Classifier: Operating System :: POSIX :: BSD :: NetBSD ++Classifier: Operating System :: POSIX :: BSD :: OpenBSD ++Classifier: Programming Language :: Python :: 2 ++Classifier: Programming Language :: Python :: 2.7 ++Classifier: Programming Language :: Python :: 3 ++Classifier: Programming Language :: Python :: 3.4 ++Classifier: Programming Language :: Python :: 3.5 ++Classifier: Programming Language :: Python :: 3.6 ++Classifier: Topic :: Software Development :: Libraries :: Python Modules ++Classifier: Topic :: System :: Operating System ++Description-Content-Type: text/markdown +diff --git a/third_party/python/distro/README.md b/third_party/python/distro/README.md +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/README.md +@@ -0,0 +1,140 @@ ++Distro - an OS platform information API ++======================================= ++ ++[![Build Status](https://travis-ci.org/nir0s/distro.svg?branch=master)](https://travis-ci.org/nir0s/distro) ++[![Build status](https://ci.appveyor.com/api/projects/status/e812qjk1gf0f74r5/branch/master?svg=true)](https://ci.appveyor.com/project/nir0s/distro/branch/master) ++[![PyPI version](http://img.shields.io/pypi/v/distro.svg)](https://pypi.python.org/pypi/distro) ++[![Supported Python Versions](https://img.shields.io/pypi/pyversions/distro.svg)](https://img.shields.io/pypi/pyversions/distro.svg) ++[![Requirements Status](https://requires.io/github/nir0s/distro/requirements.svg?branch=master)](https://requires.io/github/nir0s/distro/requirements/?branch=master) ++[![Code Coverage](https://codecov.io/github/nir0s/distro/coverage.svg?branch=master)](https://codecov.io/github/nir0s/distro?branch=master) ++[![Code Quality](https://landscape.io/github/nir0s/distro/master/landscape.svg?style=flat)](https://landscape.io/github/nir0s/distro) ++[![Is Wheel](https://img.shields.io/pypi/wheel/distro.svg?style=flat)](https://pypi.python.org/pypi/distro) ++[![Latest Github Release](https://readthedocs.org/projects/distro/badge/?version=stable)](http://distro.readthedocs.io/en/latest/) ++[![Join the chat at https://gitter.im/nir0s/distro](https://badges.gitter.im/nir0s/distro.svg)](https://gitter.im/nir0s/distro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ++ ++`distro` provides information about the ++OS distribution it runs on, such as a reliable machine-readable ID, or ++version information. ++ ++It is the recommended replacement for Python's original ++[`platform.linux_distribution`](https://docs.python.org/3.7/library/platform.html#platform.linux_distribution) ++function (which will be removed in Python 3.8). ++It also provides much more functionality which isn't necessarily Python bound, ++like a command-line interface. ++ ++Distro currently supports Linux and BSD based systems but [Windows and OS X support](https://github.com/nir0s/distro/issues/177) is also planned. ++ ++For Python 2.6 support, see https://github.com/nir0s/distro/tree/python2.6-support ++ ++## Installation ++ ++Installation of the latest released version from PyPI: ++ ++```shell ++pip install distro ++``` ++ ++Installation of the latest development version: ++ ++```shell ++pip install https://github.com/nir0s/distro/archive/master.tar.gz ++``` ++ ++ ++## Usage ++ ++```bash ++$ distro ++Name: Antergos Linux ++Version: 2015.10 (ISO-Rolling) ++Codename: ISO-Rolling ++ ++$ distro -j ++{ ++ "codename": "ISO-Rolling", ++ "id": "antergos", ++ "like": "arch", ++ "version": "16.9", ++ "version_parts": { ++ "build_number": "", ++ "major": "16", ++ "minor": "9" ++ } ++} ++ ++ ++$ python ++>>> import distro ++>>> distro.linux_distribution(full_distribution_name=False) ++('centos', '7.1.1503', 'Core') ++``` ++ ++ ++## Documentation ++ ++On top of the aforementioned API, several more functions are available. For a complete description of the ++API, see the [latest API documentation](http://distro.readthedocs.org/en/latest/). ++ ++## Background ++ ++An alternative implementation became necessary because Python 3.5 deprecated ++this function, and Python 3.8 will remove it altogether. ++Its predecessor function `platform.dist` was already deprecated since ++Python 2.6 and will also be removed in Python 3.8. ++Still, there are many cases in which access to that information is needed. ++See [Python issue 1322](https://bugs.python.org/issue1322) for more ++information. ++ ++The `distro` package implements a robust and inclusive way of retrieving the ++information about a distribution based on new standards and old methods, ++namely from these data sources (from high to low precedence): ++ ++* The os-release file `/etc/os-release`, if present. ++* The output of the `lsb_release` command, if available. ++* The distro release file (`/etc/*(-|_)(release|version)`), if present. ++* The `uname` command for BSD based distrubtions. ++ ++ ++## Python and Distribution Support ++ ++`distro` is supported and tested on Python 2.7, 3.4+ and PyPy and on ++any distribution that provides one or more of the data sources ++covered. ++ ++This package is tested with test data that mimics the exact behavior of the data sources of [a number of Linux distributions](https://github.com/nir0s/distro/tree/master/tests/resources/distros). ++ ++ ++## Testing ++ ++```shell ++git clone git@github.com:nir0s/distro.git ++cd distro ++pip install tox ++tox ++``` ++ ++ ++## Contributions ++ ++Pull requests are always welcome to deal with specific distributions or just ++for general merriment. ++ ++See [CONTRIBUTIONS](https://github.com/nir0s/distro/blob/master/CONTRIBUTING.md) for contribution info. ++ ++Reference implementations for supporting additional distributions and file ++formats can be found here: ++ ++* https://github.com/saltstack/salt/blob/develop/salt/grains/core.py#L1172 ++* https://github.com/chef/ohai/blob/master/lib/ohai/plugins/linux/platform.rb ++* https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/facts/system/distribution.py ++* https://github.com/puppetlabs/facter/blob/master/lib/src/facts/linux/os_linux.cc ++ ++## Package manager distributions ++ ++* https://src.fedoraproject.org/rpms/python-distro ++* https://www.archlinux.org/packages/community/any/python-distro/ ++* https://launchpad.net/ubuntu/+source/python-distro ++* https://packages.debian.org/sid/python-distro ++* https://packages.gentoo.org/packages/dev-python/distro ++* https://pkgs.org/download/python2-distro ++* https://slackbuilds.org/repository/14.2/python/python-distro/ +diff --git a/third_party/python/distro/dev-requirements.txt b/third_party/python/distro/dev-requirements.txt +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/dev-requirements.txt +@@ -0,0 +1,3 @@ ++pytest ++pytest-cov ++sphinx>=1.1 +\ No newline at end of file +diff --git a/third_party/python/distro/distro.py b/third_party/python/distro/distro.py +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/distro.py +@@ -0,0 +1,1216 @@ ++# Copyright 2015,2016,2017 Nir Cohen ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++""" ++The ``distro`` package (``distro`` stands for Linux Distribution) provides ++information about the Linux distribution it runs on, such as a reliable ++machine-readable distro ID, or version information. ++ ++It is the recommended replacement for Python's original ++:py:func:`platform.linux_distribution` function, but it provides much more ++functionality. An alternative implementation became necessary because Python ++3.5 deprecated this function, and Python 3.8 will remove it altogether. ++Its predecessor function :py:func:`platform.dist` was already ++deprecated since Python 2.6 and will also be removed in Python 3.8. ++Still, there are many cases in which access to OS distribution information ++is needed. See `Python issue 1322 `_ for ++more information. ++""" ++ ++import os ++import re ++import sys ++import json ++import shlex ++import logging ++import argparse ++import subprocess ++ ++ ++_UNIXCONFDIR = os.environ.get('UNIXCONFDIR', '/etc') ++_OS_RELEASE_BASENAME = 'os-release' ++ ++#: Translation table for normalizing the "ID" attribute defined in os-release ++#: files, for use by the :func:`distro.id` method. ++#: ++#: * Key: Value as defined in the os-release file, translated to lower case, ++#: with blanks translated to underscores. ++#: ++#: * Value: Normalized value. ++NORMALIZED_OS_ID = { ++ 'ol': 'oracle', # Oracle Enterprise Linux ++} ++ ++#: Translation table for normalizing the "Distributor ID" attribute returned by ++#: the lsb_release command, for use by the :func:`distro.id` method. ++#: ++#: * Key: Value as returned by the lsb_release command, translated to lower ++#: case, with blanks translated to underscores. ++#: ++#: * Value: Normalized value. ++NORMALIZED_LSB_ID = { ++ 'enterpriseenterprise': 'oracle', # Oracle Enterprise Linux ++ 'redhatenterpriseworkstation': 'rhel', # RHEL 6, 7 Workstation ++ 'redhatenterpriseserver': 'rhel', # RHEL 6, 7 Server ++} ++ ++#: Translation table for normalizing the distro ID derived from the file name ++#: of distro release files, for use by the :func:`distro.id` method. ++#: ++#: * Key: Value as derived from the file name of a distro release file, ++#: translated to lower case, with blanks translated to underscores. ++#: ++#: * Value: Normalized value. ++NORMALIZED_DISTRO_ID = { ++ 'redhat': 'rhel', # RHEL 6.x, 7.x ++} ++ ++# Pattern for content of distro release file (reversed) ++_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( ++ r'(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)') ++ ++# Pattern for base file name of distro release file ++_DISTRO_RELEASE_BASENAME_PATTERN = re.compile( ++ r'(\w+)[-_](release|version)$') ++ ++# Base file names to be ignored when searching for distro release file ++_DISTRO_RELEASE_IGNORE_BASENAMES = ( ++ 'debian_version', ++ 'lsb-release', ++ 'oem-release', ++ _OS_RELEASE_BASENAME, ++ 'system-release' ++) ++ ++ ++def linux_distribution(full_distribution_name=True): ++ """ ++ Return information about the current OS distribution as a tuple ++ ``(id_name, version, codename)`` with items as follows: ++ ++ * ``id_name``: If *full_distribution_name* is false, the result of ++ :func:`distro.id`. Otherwise, the result of :func:`distro.name`. ++ ++ * ``version``: The result of :func:`distro.version`. ++ ++ * ``codename``: The result of :func:`distro.codename`. ++ ++ The interface of this function is compatible with the original ++ :py:func:`platform.linux_distribution` function, supporting a subset of ++ its parameters. ++ ++ The data it returns may not exactly be the same, because it uses more data ++ sources than the original function, and that may lead to different data if ++ the OS distribution is not consistent across multiple data sources it ++ provides (there are indeed such distributions ...). ++ ++ Another reason for differences is the fact that the :func:`distro.id` ++ method normalizes the distro ID string to a reliable machine-readable value ++ for a number of popular OS distributions. ++ """ ++ return _distro.linux_distribution(full_distribution_name) ++ ++ ++def id(): ++ """ ++ Return the distro ID of the current distribution, as a ++ machine-readable string. ++ ++ For a number of OS distributions, the returned distro ID value is ++ *reliable*, in the sense that it is documented and that it does not change ++ across releases of the distribution. ++ ++ This package maintains the following reliable distro ID values: ++ ++ ============== ========================================= ++ Distro ID Distribution ++ ============== ========================================= ++ "ubuntu" Ubuntu ++ "debian" Debian ++ "rhel" RedHat Enterprise Linux ++ "centos" CentOS ++ "fedora" Fedora ++ "sles" SUSE Linux Enterprise Server ++ "opensuse" openSUSE ++ "amazon" Amazon Linux ++ "arch" Arch Linux ++ "cloudlinux" CloudLinux OS ++ "exherbo" Exherbo Linux ++ "gentoo" GenToo Linux ++ "ibm_powerkvm" IBM PowerKVM ++ "kvmibm" KVM for IBM z Systems ++ "linuxmint" Linux Mint ++ "mageia" Mageia ++ "mandriva" Mandriva Linux ++ "parallels" Parallels ++ "pidora" Pidora ++ "raspbian" Raspbian ++ "oracle" Oracle Linux (and Oracle Enterprise Linux) ++ "scientific" Scientific Linux ++ "slackware" Slackware ++ "xenserver" XenServer ++ "openbsd" OpenBSD ++ "netbsd" NetBSD ++ "freebsd" FreeBSD ++ ============== ========================================= ++ ++ If you have a need to get distros for reliable IDs added into this set, ++ or if you find that the :func:`distro.id` function returns a different ++ distro ID for one of the listed distros, please create an issue in the ++ `distro issue tracker`_. ++ ++ **Lookup hierarchy and transformations:** ++ ++ First, the ID is obtained from the following sources, in the specified ++ order. The first available and non-empty value is used: ++ ++ * the value of the "ID" attribute of the os-release file, ++ ++ * the value of the "Distributor ID" attribute returned by the lsb_release ++ command, ++ ++ * the first part of the file name of the distro release file, ++ ++ The so determined ID value then passes the following transformations, ++ before it is returned by this method: ++ ++ * it is translated to lower case, ++ ++ * blanks (which should not be there anyway) are translated to underscores, ++ ++ * a normalization of the ID is performed, based upon ++ `normalization tables`_. The purpose of this normalization is to ensure ++ that the ID is as reliable as possible, even across incompatible changes ++ in the OS distributions. A common reason for an incompatible change is ++ the addition of an os-release file, or the addition of the lsb_release ++ command, with ID values that differ from what was previously determined ++ from the distro release file name. ++ """ ++ return _distro.id() ++ ++ ++def name(pretty=False): ++ """ ++ Return the name of the current OS distribution, as a human-readable ++ string. ++ ++ If *pretty* is false, the name is returned without version or codename. ++ (e.g. "CentOS Linux") ++ ++ If *pretty* is true, the version and codename are appended. ++ (e.g. "CentOS Linux 7.1.1503 (Core)") ++ ++ **Lookup hierarchy:** ++ ++ The name is obtained from the following sources, in the specified order. ++ The first available and non-empty value is used: ++ ++ * If *pretty* is false: ++ ++ - the value of the "NAME" attribute of the os-release file, ++ ++ - the value of the "Distributor ID" attribute returned by the lsb_release ++ command, ++ ++ - the value of the "" field of the distro release file. ++ ++ * If *pretty* is true: ++ ++ - the value of the "PRETTY_NAME" attribute of the os-release file, ++ ++ - the value of the "Description" attribute returned by the lsb_release ++ command, ++ ++ - the value of the "" field of the distro release file, appended ++ with the value of the pretty version ("" and "" ++ fields) of the distro release file, if available. ++ """ ++ return _distro.name(pretty) ++ ++ ++def version(pretty=False, best=False): ++ """ ++ Return the version of the current OS distribution, as a human-readable ++ string. ++ ++ If *pretty* is false, the version is returned without codename (e.g. ++ "7.0"). ++ ++ If *pretty* is true, the codename in parenthesis is appended, if the ++ codename is non-empty (e.g. "7.0 (Maipo)"). ++ ++ Some distributions provide version numbers with different precisions in ++ the different sources of distribution information. Examining the different ++ sources in a fixed priority order does not always yield the most precise ++ version (e.g. for Debian 8.2, or CentOS 7.1). ++ ++ The *best* parameter can be used to control the approach for the returned ++ version: ++ ++ If *best* is false, the first non-empty version number in priority order of ++ the examined sources is returned. ++ ++ If *best* is true, the most precise version number out of all examined ++ sources is returned. ++ ++ **Lookup hierarchy:** ++ ++ In all cases, the version number is obtained from the following sources. ++ If *best* is false, this order represents the priority order: ++ ++ * the value of the "VERSION_ID" attribute of the os-release file, ++ * the value of the "Release" attribute returned by the lsb_release ++ command, ++ * the version number parsed from the "" field of the first line ++ of the distro release file, ++ * the version number parsed from the "PRETTY_NAME" attribute of the ++ os-release file, if it follows the format of the distro release files. ++ * the version number parsed from the "Description" attribute returned by ++ the lsb_release command, if it follows the format of the distro release ++ files. ++ """ ++ return _distro.version(pretty, best) ++ ++ ++def version_parts(best=False): ++ """ ++ Return the version of the current OS distribution as a tuple ++ ``(major, minor, build_number)`` with items as follows: ++ ++ * ``major``: The result of :func:`distro.major_version`. ++ ++ * ``minor``: The result of :func:`distro.minor_version`. ++ ++ * ``build_number``: The result of :func:`distro.build_number`. ++ ++ For a description of the *best* parameter, see the :func:`distro.version` ++ method. ++ """ ++ return _distro.version_parts(best) ++ ++ ++def major_version(best=False): ++ """ ++ Return the major version of the current OS distribution, as a string, ++ if provided. ++ Otherwise, the empty string is returned. The major version is the first ++ part of the dot-separated version string. ++ ++ For a description of the *best* parameter, see the :func:`distro.version` ++ method. ++ """ ++ return _distro.major_version(best) ++ ++ ++def minor_version(best=False): ++ """ ++ Return the minor version of the current OS distribution, as a string, ++ if provided. ++ Otherwise, the empty string is returned. The minor version is the second ++ part of the dot-separated version string. ++ ++ For a description of the *best* parameter, see the :func:`distro.version` ++ method. ++ """ ++ return _distro.minor_version(best) ++ ++ ++def build_number(best=False): ++ """ ++ Return the build number of the current OS distribution, as a string, ++ if provided. ++ Otherwise, the empty string is returned. The build number is the third part ++ of the dot-separated version string. ++ ++ For a description of the *best* parameter, see the :func:`distro.version` ++ method. ++ """ ++ return _distro.build_number(best) ++ ++ ++def like(): ++ """ ++ Return a space-separated list of distro IDs of distributions that are ++ closely related to the current OS distribution in regards to packaging ++ and programming interfaces, for example distributions the current ++ distribution is a derivative from. ++ ++ **Lookup hierarchy:** ++ ++ This information item is only provided by the os-release file. ++ For details, see the description of the "ID_LIKE" attribute in the ++ `os-release man page ++ `_. ++ """ ++ return _distro.like() ++ ++ ++def codename(): ++ """ ++ Return the codename for the release of the current OS distribution, ++ as a string. ++ ++ If the distribution does not have a codename, an empty string is returned. ++ ++ Note that the returned codename is not always really a codename. For ++ example, openSUSE returns "x86_64". This function does not handle such ++ cases in any special way and just returns the string it finds, if any. ++ ++ **Lookup hierarchy:** ++ ++ * the codename within the "VERSION" attribute of the os-release file, if ++ provided, ++ ++ * the value of the "Codename" attribute returned by the lsb_release ++ command, ++ ++ * the value of the "" field of the distro release file. ++ """ ++ return _distro.codename() ++ ++ ++def info(pretty=False, best=False): ++ """ ++ Return certain machine-readable information items about the current OS ++ distribution in a dictionary, as shown in the following example: ++ ++ .. sourcecode:: python ++ ++ { ++ 'id': 'rhel', ++ 'version': '7.0', ++ 'version_parts': { ++ 'major': '7', ++ 'minor': '0', ++ 'build_number': '' ++ }, ++ 'like': 'fedora', ++ 'codename': 'Maipo' ++ } ++ ++ The dictionary structure and keys are always the same, regardless of which ++ information items are available in the underlying data sources. The values ++ for the various keys are as follows: ++ ++ * ``id``: The result of :func:`distro.id`. ++ ++ * ``version``: The result of :func:`distro.version`. ++ ++ * ``version_parts -> major``: The result of :func:`distro.major_version`. ++ ++ * ``version_parts -> minor``: The result of :func:`distro.minor_version`. ++ ++ * ``version_parts -> build_number``: The result of ++ :func:`distro.build_number`. ++ ++ * ``like``: The result of :func:`distro.like`. ++ ++ * ``codename``: The result of :func:`distro.codename`. ++ ++ For a description of the *pretty* and *best* parameters, see the ++ :func:`distro.version` method. ++ """ ++ return _distro.info(pretty, best) ++ ++ ++def os_release_info(): ++ """ ++ Return a dictionary containing key-value pairs for the information items ++ from the os-release file data source of the current OS distribution. ++ ++ See `os-release file`_ for details about these information items. ++ """ ++ return _distro.os_release_info() ++ ++ ++def lsb_release_info(): ++ """ ++ Return a dictionary containing key-value pairs for the information items ++ from the lsb_release command data source of the current OS distribution. ++ ++ See `lsb_release command output`_ for details about these information ++ items. ++ """ ++ return _distro.lsb_release_info() ++ ++ ++def distro_release_info(): ++ """ ++ Return a dictionary containing key-value pairs for the information items ++ from the distro release file data source of the current OS distribution. ++ ++ See `distro release file`_ for details about these information items. ++ """ ++ return _distro.distro_release_info() ++ ++ ++def uname_info(): ++ """ ++ Return a dictionary containing key-value pairs for the information items ++ from the distro release file data source of the current OS distribution. ++ """ ++ return _distro.uname_info() ++ ++ ++def os_release_attr(attribute): ++ """ ++ Return a single named information item from the os-release file data source ++ of the current OS distribution. ++ ++ Parameters: ++ ++ * ``attribute`` (string): Key of the information item. ++ ++ Returns: ++ ++ * (string): Value of the information item, if the item exists. ++ The empty string, if the item does not exist. ++ ++ See `os-release file`_ for details about these information items. ++ """ ++ return _distro.os_release_attr(attribute) ++ ++ ++def lsb_release_attr(attribute): ++ """ ++ Return a single named information item from the lsb_release command output ++ data source of the current OS distribution. ++ ++ Parameters: ++ ++ * ``attribute`` (string): Key of the information item. ++ ++ Returns: ++ ++ * (string): Value of the information item, if the item exists. ++ The empty string, if the item does not exist. ++ ++ See `lsb_release command output`_ for details about these information ++ items. ++ """ ++ return _distro.lsb_release_attr(attribute) ++ ++ ++def distro_release_attr(attribute): ++ """ ++ Return a single named information item from the distro release file ++ data source of the current OS distribution. ++ ++ Parameters: ++ ++ * ``attribute`` (string): Key of the information item. ++ ++ Returns: ++ ++ * (string): Value of the information item, if the item exists. ++ The empty string, if the item does not exist. ++ ++ See `distro release file`_ for details about these information items. ++ """ ++ return _distro.distro_release_attr(attribute) ++ ++ ++def uname_attr(attribute): ++ """ ++ Return a single named information item from the distro release file ++ data source of the current OS distribution. ++ ++ Parameters: ++ ++ * ``attribute`` (string): Key of the information item. ++ ++ Returns: ++ ++ * (string): Value of the information item, if the item exists. ++ The empty string, if the item does not exist. ++ """ ++ return _distro.uname_attr(attribute) ++ ++ ++class cached_property(object): ++ """A version of @property which caches the value. On access, it calls the ++ underlying function and sets the value in `__dict__` so future accesses ++ will not re-call the property. ++ """ ++ def __init__(self, f): ++ self._fname = f.__name__ ++ self._f = f ++ ++ def __get__(self, obj, owner): ++ assert obj is not None, 'call {} on an instance'.format(self._fname) ++ ret = obj.__dict__[self._fname] = self._f(obj) ++ return ret ++ ++ ++class LinuxDistribution(object): ++ """ ++ Provides information about a OS distribution. ++ ++ This package creates a private module-global instance of this class with ++ default initialization arguments, that is used by the ++ `consolidated accessor functions`_ and `single source accessor functions`_. ++ By using default initialization arguments, that module-global instance ++ returns data about the current OS distribution (i.e. the distro this ++ package runs on). ++ ++ Normally, it is not necessary to create additional instances of this class. ++ However, in situations where control is needed over the exact data sources ++ that are used, instances of this class can be created with a specific ++ distro release file, or a specific os-release file, or without invoking the ++ lsb_release command. ++ """ ++ ++ def __init__(self, ++ include_lsb=True, ++ os_release_file='', ++ distro_release_file='', ++ include_uname=True): ++ """ ++ The initialization method of this class gathers information from the ++ available data sources, and stores that in private instance attributes. ++ Subsequent access to the information items uses these private instance ++ attributes, so that the data sources are read only once. ++ ++ Parameters: ++ ++ * ``include_lsb`` (bool): Controls whether the ++ `lsb_release command output`_ is included as a data source. ++ ++ If the lsb_release command is not available in the program execution ++ path, the data source for the lsb_release command will be empty. ++ ++ * ``os_release_file`` (string): The path name of the ++ `os-release file`_ that is to be used as a data source. ++ ++ An empty string (the default) will cause the default path name to ++ be used (see `os-release file`_ for details). ++ ++ If the specified or defaulted os-release file does not exist, the ++ data source for the os-release file will be empty. ++ ++ * ``distro_release_file`` (string): The path name of the ++ `distro release file`_ that is to be used as a data source. ++ ++ An empty string (the default) will cause a default search algorithm ++ to be used (see `distro release file`_ for details). ++ ++ If the specified distro release file does not exist, or if no default ++ distro release file can be found, the data source for the distro ++ release file will be empty. ++ ++ * ``include_name`` (bool): Controls whether uname command output is ++ included as a data source. If the uname command is not available in ++ the program execution path the data source for the uname command will ++ be empty. ++ ++ Public instance attributes: ++ ++ * ``os_release_file`` (string): The path name of the ++ `os-release file`_ that is actually used as a data source. The ++ empty string if no distro release file is used as a data source. ++ ++ * ``distro_release_file`` (string): The path name of the ++ `distro release file`_ that is actually used as a data source. The ++ empty string if no distro release file is used as a data source. ++ ++ * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter. ++ This controls whether the lsb information will be loaded. ++ ++ * ``include_uname`` (bool): The result of the ``include_uname`` ++ parameter. This controls whether the uname information will ++ be loaded. ++ ++ Raises: ++ ++ * :py:exc:`IOError`: Some I/O issue with an os-release file or distro ++ release file. ++ ++ * :py:exc:`subprocess.CalledProcessError`: The lsb_release command had ++ some issue (other than not being available in the program execution ++ path). ++ ++ * :py:exc:`UnicodeError`: A data source has unexpected characters or ++ uses an unexpected encoding. ++ """ ++ self.os_release_file = os_release_file or \ ++ os.path.join(_UNIXCONFDIR, _OS_RELEASE_BASENAME) ++ self.distro_release_file = distro_release_file or '' # updated later ++ self.include_lsb = include_lsb ++ self.include_uname = include_uname ++ ++ def __repr__(self): ++ """Return repr of all info ++ """ ++ return \ ++ "LinuxDistribution(" \ ++ "os_release_file={self.os_release_file!r}, " \ ++ "distro_release_file={self.distro_release_file!r}, " \ ++ "include_lsb={self.include_lsb!r}, " \ ++ "include_uname={self.include_uname!r}, " \ ++ "_os_release_info={self._os_release_info!r}, " \ ++ "_lsb_release_info={self._lsb_release_info!r}, " \ ++ "_distro_release_info={self._distro_release_info!r}, " \ ++ "_uname_info={self._uname_info!r})".format( ++ self=self) ++ ++ def linux_distribution(self, full_distribution_name=True): ++ """ ++ Return information about the OS distribution that is compatible ++ with Python's :func:`platform.linux_distribution`, supporting a subset ++ of its parameters. ++ ++ For details, see :func:`distro.linux_distribution`. ++ """ ++ return ( ++ self.name() if full_distribution_name else self.id(), ++ self.version(), ++ self.codename() ++ ) ++ ++ def id(self): ++ """Return the distro ID of the OS distribution, as a string. ++ ++ For details, see :func:`distro.id`. ++ """ ++ def normalize(distro_id, table): ++ distro_id = distro_id.lower().replace(' ', '_') ++ return table.get(distro_id, distro_id) ++ ++ distro_id = self.os_release_attr('id') ++ if distro_id: ++ return normalize(distro_id, NORMALIZED_OS_ID) ++ ++ distro_id = self.lsb_release_attr('distributor_id') ++ if distro_id: ++ return normalize(distro_id, NORMALIZED_LSB_ID) ++ ++ distro_id = self.distro_release_attr('id') ++ if distro_id: ++ return normalize(distro_id, NORMALIZED_DISTRO_ID) ++ ++ distro_id = self.uname_attr('id') ++ if distro_id: ++ return normalize(distro_id, NORMALIZED_DISTRO_ID) ++ ++ return '' ++ ++ def name(self, pretty=False): ++ """ ++ Return the name of the OS distribution, as a string. ++ ++ For details, see :func:`distro.name`. ++ """ ++ name = self.os_release_attr('name') \ ++ or self.lsb_release_attr('distributor_id') \ ++ or self.distro_release_attr('name') \ ++ or self.uname_attr('name') ++ if pretty: ++ name = self.os_release_attr('pretty_name') \ ++ or self.lsb_release_attr('description') ++ if not name: ++ name = self.distro_release_attr('name') \ ++ or self.uname_attr('name') ++ version = self.version(pretty=True) ++ if version: ++ name = name + ' ' + version ++ return name or '' ++ ++ def version(self, pretty=False, best=False): ++ """ ++ Return the version of the OS distribution, as a string. ++ ++ For details, see :func:`distro.version`. ++ """ ++ versions = [ ++ self.os_release_attr('version_id'), ++ self.lsb_release_attr('release'), ++ self.distro_release_attr('version_id'), ++ self._parse_distro_release_content( ++ self.os_release_attr('pretty_name')).get('version_id', ''), ++ self._parse_distro_release_content( ++ self.lsb_release_attr('description')).get('version_id', ''), ++ self.uname_attr('release') ++ ] ++ version = '' ++ if best: ++ # This algorithm uses the last version in priority order that has ++ # the best precision. If the versions are not in conflict, that ++ # does not matter; otherwise, using the last one instead of the ++ # first one might be considered a surprise. ++ for v in versions: ++ if v.count(".") > version.count(".") or version == '': ++ version = v ++ else: ++ for v in versions: ++ if v != '': ++ version = v ++ break ++ if pretty and version and self.codename(): ++ version = u'{0} ({1})'.format(version, self.codename()) ++ return version ++ ++ def version_parts(self, best=False): ++ """ ++ Return the version of the OS distribution, as a tuple of version ++ numbers. ++ ++ For details, see :func:`distro.version_parts`. ++ """ ++ version_str = self.version(best=best) ++ if version_str: ++ version_regex = re.compile(r'(\d+)\.?(\d+)?\.?(\d+)?') ++ matches = version_regex.match(version_str) ++ if matches: ++ major, minor, build_number = matches.groups() ++ return major, minor or '', build_number or '' ++ return '', '', '' ++ ++ def major_version(self, best=False): ++ """ ++ Return the major version number of the current distribution. ++ ++ For details, see :func:`distro.major_version`. ++ """ ++ return self.version_parts(best)[0] ++ ++ def minor_version(self, best=False): ++ """ ++ Return the minor version number of the current distribution. ++ ++ For details, see :func:`distro.minor_version`. ++ """ ++ return self.version_parts(best)[1] ++ ++ def build_number(self, best=False): ++ """ ++ Return the build number of the current distribution. ++ ++ For details, see :func:`distro.build_number`. ++ """ ++ return self.version_parts(best)[2] ++ ++ def like(self): ++ """ ++ Return the IDs of distributions that are like the OS distribution. ++ ++ For details, see :func:`distro.like`. ++ """ ++ return self.os_release_attr('id_like') or '' ++ ++ def codename(self): ++ """ ++ Return the codename of the OS distribution. ++ ++ For details, see :func:`distro.codename`. ++ """ ++ try: ++ # Handle os_release specially since distros might purposefully set ++ # this to empty string to have no codename ++ return self._os_release_info['codename'] ++ except KeyError: ++ return self.lsb_release_attr('codename') \ ++ or self.distro_release_attr('codename') \ ++ or '' ++ ++ def info(self, pretty=False, best=False): ++ """ ++ Return certain machine-readable information about the OS ++ distribution. ++ ++ For details, see :func:`distro.info`. ++ """ ++ return dict( ++ id=self.id(), ++ version=self.version(pretty, best), ++ version_parts=dict( ++ major=self.major_version(best), ++ minor=self.minor_version(best), ++ build_number=self.build_number(best) ++ ), ++ like=self.like(), ++ codename=self.codename(), ++ ) ++ ++ def os_release_info(self): ++ """ ++ Return a dictionary containing key-value pairs for the information ++ items from the os-release file data source of the OS distribution. ++ ++ For details, see :func:`distro.os_release_info`. ++ """ ++ return self._os_release_info ++ ++ def lsb_release_info(self): ++ """ ++ Return a dictionary containing key-value pairs for the information ++ items from the lsb_release command data source of the OS ++ distribution. ++ ++ For details, see :func:`distro.lsb_release_info`. ++ """ ++ return self._lsb_release_info ++ ++ def distro_release_info(self): ++ """ ++ Return a dictionary containing key-value pairs for the information ++ items from the distro release file data source of the OS ++ distribution. ++ ++ For details, see :func:`distro.distro_release_info`. ++ """ ++ return self._distro_release_info ++ ++ def uname_info(self): ++ """ ++ Return a dictionary containing key-value pairs for the information ++ items from the uname command data source of the OS distribution. ++ ++ For details, see :func:`distro.uname_info`. ++ """ ++ return self._uname_info ++ ++ def os_release_attr(self, attribute): ++ """ ++ Return a single named information item from the os-release file data ++ source of the OS distribution. ++ ++ For details, see :func:`distro.os_release_attr`. ++ """ ++ return self._os_release_info.get(attribute, '') ++ ++ def lsb_release_attr(self, attribute): ++ """ ++ Return a single named information item from the lsb_release command ++ output data source of the OS distribution. ++ ++ For details, see :func:`distro.lsb_release_attr`. ++ """ ++ return self._lsb_release_info.get(attribute, '') ++ ++ def distro_release_attr(self, attribute): ++ """ ++ Return a single named information item from the distro release file ++ data source of the OS distribution. ++ ++ For details, see :func:`distro.distro_release_attr`. ++ """ ++ return self._distro_release_info.get(attribute, '') ++ ++ def uname_attr(self, attribute): ++ """ ++ Return a single named information item from the uname command ++ output data source of the OS distribution. ++ ++ For details, see :func:`distro.uname_release_attr`. ++ """ ++ return self._uname_info.get(attribute, '') ++ ++ @cached_property ++ def _os_release_info(self): ++ """ ++ Get the information items from the specified os-release file. ++ ++ Returns: ++ A dictionary containing all information items. ++ """ ++ if os.path.isfile(self.os_release_file): ++ with open(self.os_release_file) as release_file: ++ return self._parse_os_release_content(release_file) ++ return {} ++ ++ @staticmethod ++ def _parse_os_release_content(lines): ++ """ ++ Parse the lines of an os-release file. ++ ++ Parameters: ++ ++ * lines: Iterable through the lines in the os-release file. ++ Each line must be a unicode string or a UTF-8 encoded byte ++ string. ++ ++ Returns: ++ A dictionary containing all information items. ++ """ ++ props = {} ++ lexer = shlex.shlex(lines, posix=True) ++ lexer.whitespace_split = True ++ ++ # The shlex module defines its `wordchars` variable using literals, ++ # making it dependent on the encoding of the Python source file. ++ # In Python 2.6 and 2.7, the shlex source file is encoded in ++ # 'iso-8859-1', and the `wordchars` variable is defined as a byte ++ # string. This causes a UnicodeDecodeError to be raised when the ++ # parsed content is a unicode object. The following fix resolves that ++ # (... but it should be fixed in shlex...): ++ if sys.version_info[0] == 2 and isinstance(lexer.wordchars, bytes): ++ lexer.wordchars = lexer.wordchars.decode('iso-8859-1') ++ ++ tokens = list(lexer) ++ for token in tokens: ++ # At this point, all shell-like parsing has been done (i.e. ++ # comments processed, quotes and backslash escape sequences ++ # processed, multi-line values assembled, trailing newlines ++ # stripped, etc.), so the tokens are now either: ++ # * variable assignments: var=value ++ # * commands or their arguments (not allowed in os-release) ++ if '=' in token: ++ k, v = token.split('=', 1) ++ if isinstance(v, bytes): ++ v = v.decode('utf-8') ++ props[k.lower()] = v ++ else: ++ # Ignore any tokens that are not variable assignments ++ pass ++ ++ if 'version_codename' in props: ++ # os-release added a version_codename field. Use that in ++ # preference to anything else Note that some distros purposefully ++ # do not have code names. They should be setting ++ # version_codename="" ++ props['codename'] = props['version_codename'] ++ elif 'ubuntu_codename' in props: ++ # Same as above but a non-standard field name used on older Ubuntus ++ props['codename'] = props['ubuntu_codename'] ++ elif 'version' in props: ++ # If there is no version_codename, parse it from the version ++ codename = re.search(r'(\(\D+\))|,(\s+)?\D+', props['version']) ++ if codename: ++ codename = codename.group() ++ codename = codename.strip('()') ++ codename = codename.strip(',') ++ codename = codename.strip() ++ # codename appears within paranthese. ++ props['codename'] = codename ++ ++ return props ++ ++ @cached_property ++ def _lsb_release_info(self): ++ """ ++ Get the information items from the lsb_release command output. ++ ++ Returns: ++ A dictionary containing all information items. ++ """ ++ if not self.include_lsb: ++ return {} ++ with open(os.devnull, 'w') as devnull: ++ try: ++ cmd = ('lsb_release', '-a') ++ stdout = subprocess.check_output(cmd, stderr=devnull) ++ except OSError: # Command not found ++ return {} ++ content = stdout.decode(sys.getfilesystemencoding()).splitlines() ++ return self._parse_lsb_release_content(content) ++ ++ @staticmethod ++ def _parse_lsb_release_content(lines): ++ """ ++ Parse the output of the lsb_release command. ++ ++ Parameters: ++ ++ * lines: Iterable through the lines of the lsb_release output. ++ Each line must be a unicode string or a UTF-8 encoded byte ++ string. ++ ++ Returns: ++ A dictionary containing all information items. ++ """ ++ props = {} ++ for line in lines: ++ kv = line.strip('\n').split(':', 1) ++ if len(kv) != 2: ++ # Ignore lines without colon. ++ continue ++ k, v = kv ++ props.update({k.replace(' ', '_').lower(): v.strip()}) ++ return props ++ ++ @cached_property ++ def _uname_info(self): ++ with open(os.devnull, 'w') as devnull: ++ try: ++ cmd = ('uname', '-rs') ++ stdout = subprocess.check_output(cmd, stderr=devnull) ++ except OSError: ++ return {} ++ content = stdout.decode(sys.getfilesystemencoding()).splitlines() ++ return self._parse_uname_content(content) ++ ++ @staticmethod ++ def _parse_uname_content(lines): ++ props = {} ++ match = re.search(r'^([^\s]+)\s+([\d\.]+)', lines[0].strip()) ++ if match: ++ name, version = match.groups() ++ ++ # This is to prevent the Linux kernel version from ++ # appearing as the 'best' version on otherwise ++ # identifiable distributions. ++ if name == 'Linux': ++ return {} ++ props['id'] = name.lower() ++ props['name'] = name ++ props['release'] = version ++ return props ++ ++ @cached_property ++ def _distro_release_info(self): ++ """ ++ Get the information items from the specified distro release file. ++ ++ Returns: ++ A dictionary containing all information items. ++ """ ++ if self.distro_release_file: ++ # If it was specified, we use it and parse what we can, even if ++ # its file name or content does not match the expected pattern. ++ distro_info = self._parse_distro_release_file( ++ self.distro_release_file) ++ basename = os.path.basename(self.distro_release_file) ++ # The file name pattern for user-specified distro release files ++ # is somewhat more tolerant (compared to when searching for the ++ # file), because we want to use what was specified as best as ++ # possible. ++ match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) ++ if 'name' in distro_info \ ++ and 'cloudlinux' in distro_info['name'].lower(): ++ distro_info['id'] = 'cloudlinux' ++ elif match: ++ distro_info['id'] = match.group(1) ++ return distro_info ++ else: ++ try: ++ basenames = os.listdir(_UNIXCONFDIR) ++ # We sort for repeatability in cases where there are multiple ++ # distro specific files; e.g. CentOS, Oracle, Enterprise all ++ # containing `redhat-release` on top of their own. ++ basenames.sort() ++ except OSError: ++ # This may occur when /etc is not readable but we can't be ++ # sure about the *-release files. Check common entries of ++ # /etc for information. If they turn out to not be there the ++ # error is handled in `_parse_distro_release_file()`. ++ basenames = ['SuSE-release', ++ 'arch-release', ++ 'base-release', ++ 'centos-release', ++ 'fedora-release', ++ 'gentoo-release', ++ 'mageia-release', ++ 'mandrake-release', ++ 'mandriva-release', ++ 'mandrivalinux-release', ++ 'manjaro-release', ++ 'oracle-release', ++ 'redhat-release', ++ 'sl-release', ++ 'slackware-version'] ++ for basename in basenames: ++ if basename in _DISTRO_RELEASE_IGNORE_BASENAMES: ++ continue ++ match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) ++ if match: ++ filepath = os.path.join(_UNIXCONFDIR, basename) ++ distro_info = self._parse_distro_release_file(filepath) ++ if 'name' in distro_info: ++ # The name is always present if the pattern matches ++ self.distro_release_file = filepath ++ distro_info['id'] = match.group(1) ++ if 'cloudlinux' in distro_info['name'].lower(): ++ distro_info['id'] = 'cloudlinux' ++ return distro_info ++ return {} ++ ++ def _parse_distro_release_file(self, filepath): ++ """ ++ Parse a distro release file. ++ ++ Parameters: ++ ++ * filepath: Path name of the distro release file. ++ ++ Returns: ++ A dictionary containing all information items. ++ """ ++ try: ++ with open(filepath) as fp: ++ # Only parse the first line. For instance, on SLES there ++ # are multiple lines. We don't want them... ++ return self._parse_distro_release_content(fp.readline()) ++ except (OSError, IOError): ++ # Ignore not being able to read a specific, seemingly version ++ # related file. ++ # See https://github.com/nir0s/distro/issues/162 ++ return {} ++ ++ @staticmethod ++ def _parse_distro_release_content(line): ++ """ ++ Parse a line from a distro release file. ++ ++ Parameters: ++ * line: Line from the distro release file. Must be a unicode string ++ or a UTF-8 encoded byte string. ++ ++ Returns: ++ A dictionary containing all information items. ++ """ ++ if isinstance(line, bytes): ++ line = line.decode('utf-8') ++ matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match( ++ line.strip()[::-1]) ++ distro_info = {} ++ if matches: ++ # regexp ensures non-None ++ distro_info['name'] = matches.group(3)[::-1] ++ if matches.group(2): ++ distro_info['version_id'] = matches.group(2)[::-1] ++ if matches.group(1): ++ distro_info['codename'] = matches.group(1)[::-1] ++ elif line: ++ distro_info['name'] = line.strip() ++ return distro_info ++ ++ ++_distro = LinuxDistribution() ++ ++ ++def main(): ++ logger = logging.getLogger(__name__) ++ logger.setLevel(logging.DEBUG) ++ logger.addHandler(logging.StreamHandler(sys.stdout)) ++ ++ parser = argparse.ArgumentParser(description="OS distro info tool") ++ parser.add_argument( ++ '--json', ++ '-j', ++ help="Output in machine readable format", ++ action="store_true") ++ args = parser.parse_args() ++ ++ if args.json: ++ logger.info(json.dumps(info(), indent=4, sort_keys=True)) ++ else: ++ logger.info('Name: %s', name(pretty=True)) ++ distribution_version = version(pretty=True) ++ logger.info('Version: %s', distribution_version) ++ distribution_codename = codename() ++ logger.info('Codename: %s', distribution_codename) ++ ++ ++if __name__ == '__main__': ++ main() +diff --git a/third_party/python/distro/docs/conf.py b/third_party/python/distro/docs/conf.py +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/docs/conf.py +@@ -0,0 +1,342 @@ ++# -*- coding: utf-8 -*- ++# ++# Configuration file for Sphinx builds, created by ++# sphinx-quickstart on Wed Mar 2 11:33:06 2016. ++# ++# This file is execfile()d with the current directory set to its ++# containing dir. ++# ++# Note that not all possible configuration values are present in this ++# autogenerated file. ++# ++# All configuration values have a default; values that are commented out ++# serve to show the default. ++ ++import sys ++import os ++import re ++ ++# If extensions (or modules to document with autodoc) are in another directory, ++# add these directories to sys.path here. If the directory is relative to the ++# documentation root, use os.path.abspath to make it absolute, like shown here. ++sys.path.insert(0, os.path.abspath('..')) ++ ++# -- General configuration ------------------------------------------------ ++ ++# If your documentation needs a minimal Sphinx version, state it here. ++needs_sphinx = '1.1' ++ ++# Add any Sphinx extension module names here, as strings. They can be ++# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ++# ones. ++extensions = [ ++ 'sphinx.ext.autodoc', ++ 'sphinx.ext.intersphinx', ++ 'sphinx.ext.todo', ++ 'sphinx.ext.coverage', ++ 'sphinx.ext.viewcode', ++] ++ ++# Add any paths that contain templates here, relative to this directory. ++templates_path = ['_templates'] ++ ++# The suffix(es) of source filenames. ++# You can specify multiple suffix as a list of string: ++# source_suffix = ['.rst', '.md'] ++source_suffix = '.rst' ++ ++# The encoding of source files. ++source_encoding = 'utf-8' ++ ++# The master toctree document. ++on_rtd = os.environ.get('READTHEDOCS', None) == 'True' ++if on_rtd: ++ master_doc = 'index' ++else: ++ master_doc = 'docs/index' ++ ++# General information about the project. ++project = u'distro' ++copyright = u'2015,2016, Nir Cohen, Andreas Maier' ++author = u'Nir Cohen, Andreas Maier' ++ ++# The short description of the package. ++_short_description = u'Linux Distribution - a Linux OS platform information API' ++ ++# The version info for the project you're documenting, acts as replacement for ++# |version| and |release|, also used in various other places throughout the ++# built documents. ++ ++def parse_version(): ++ with open('../setup.py', 'r') as _fp: ++ _lines = _fp.readlines() ++ for _line in _lines: ++ m = re.match(r'^package_version *= *[\'"](.+)[\'"].*$', _line) ++ if m: ++ break ++ if m: ++ return m.group(1) ++ else: ++ return 'unknown' ++ ++# The short X.Y version. ++# Note: We use the full version in both cases. ++version = parse_version() ++ ++# The full version, including alpha/beta/rc tags. ++release = version ++ ++# The language for content autogenerated by Sphinx. Refer to documentation ++# for a list of supported languages. ++# ++# This is also used if you do content translation via gettext catalogs. ++# Usually you set "language" from the command line for these cases. ++language = None ++ ++# There are two options for replacing |today|: either, you set today to some ++# non-false value, then it is used: ++#today = '' ++# Else, today_fmt is used as the format for a strftime call. ++#today_fmt = '%B %d, %Y' ++ ++# List of patterns, relative to source directory, that match files and ++# directories to ignore when looking for source files. ++exclude_patterns = ["tests", ".tox", ".git", "build_docs", "ld.egg-info"] ++ ++# The reST default role (used for this markup: `text`) to use for all ++# documents. ++#default_role = None ++ ++# If true, '()' will be appended to :func: etc. cross-reference text. ++add_function_parentheses = True ++ ++# If true, the current module name will be prepended to all description ++# unit titles (such as .. function::). ++#add_module_names = True ++ ++# If true, sectionauthor and moduleauthor directives will be shown in the ++# output. They are ignored by default. ++#show_authors = False ++ ++# The name of the Pygments (syntax highlighting) style to use. ++pygments_style = 'sphinx' ++ ++# A list of ignored prefixes for module index sorting. ++#modindex_common_prefix = [] ++ ++# If true, keep warnings as "system message" paragraphs in the built documents. ++#keep_warnings = False ++ ++# If true, `todo` and `todoList` produce output, else they produce nothing. ++todo_include_todos = True ++ ++ ++# -- Options for HTML output ---------------------------------------------- ++ ++# The theme to use for HTML and HTML Help pages. ++# See http://www.sphinx-doc.org/en/stable/theming.html for built-in themes. ++html_theme = "classic" ++ ++# Theme options are theme-specific and customize the look and feel of a theme ++# further. ++# See http://www.sphinx-doc.org/en/stable/theming.html for the options ++# available for built-in themes. ++html_theme_options = { ++} ++ ++# Add any paths that contain custom themes here, relative to this directory. ++#html_theme_path = [] ++ ++# The name for this set of Sphinx documents. If not defined, it defaults to ++# " v documentation". ++#html_title = None ++ ++# A shorter title for the navigation bar. Default is the same as html_title. ++#html_short_title = 'distro' ++ ++# The name of an image file (relative to this directory) to place at the top ++# of the sidebar. ++#html_logo = None ++ ++# The name of an image file (relative to this directory) to use as a favicon of ++# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 ++# pixels large. ++#html_favicon = None ++ ++# Add any paths that contain custom static files (such as style sheets) here, ++# relative to this directory. They are copied after the builtin static files, ++# so a file named "default.css" will overwrite the builtin "default.css". ++html_static_path = ['html_static'] ++ ++# Add any extra paths that contain custom files (such as robots.txt or ++# .htaccess) here, relative to this directory. These files are copied ++# directly to the root of the documentation. ++html_extra_path = ['html_extra'] ++ ++# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, ++# using the given strftime format. ++#html_last_updated_fmt = '%b %d, %Y' ++ ++# If true, SmartyPants will be used to convert quotes and dashes to ++# typographically correct entities. ++#html_use_smartypants = True ++ ++# Custom sidebar templates, maps document names to template names. ++#html_sidebars = {} ++ ++# Additional templates that should be rendered to pages, maps page names to ++# template names. ++#html_additional_pages = {} ++ ++# If false, no module index is generated. ++#html_domain_indices = True ++ ++# If false, no index is generated. ++#html_use_index = True ++ ++# If true, the index is split into individual pages for each letter. ++#html_split_index = False ++ ++# If true, links to the reST sources are added to the pages. ++#html_show_sourcelink = True ++ ++# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. ++#html_show_sphinx = True ++ ++# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. ++#html_show_copyright = True ++ ++# If true, an OpenSearch description file will be output, and all pages will ++# contain a tag referring to it. The value of this option must be the ++# base URL from which the finished HTML is served. ++#html_use_opensearch = '' ++ ++# This is the file name suffix for HTML files (e.g. ".xhtml"). ++#html_file_suffix = None ++ ++# Language to be used for generating the HTML full-text search index. ++# Sphinx supports the following languages: ++# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' ++# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' ++#html_search_language = 'en' ++ ++# A dictionary with options for the search language support, empty by default. ++# Now only 'ja' uses this config value ++#html_search_options = {'type': 'default'} ++ ++# The name of a javascript file (relative to the configuration directory) that ++# implements a search results scorer. If empty, the default will be used. ++#html_search_scorer = 'scorer.js' ++ ++# Output file base name for HTML help builder. ++htmlhelp_basename = 'distro_doc' ++ ++# -- Options for LaTeX output --------------------------------------------- ++ ++latex_elements = { ++# The paper size ('letterpaper' or 'a4paper'). ++#'papersize': 'letterpaper', ++ ++# The font size ('10pt', '11pt' or '12pt'). ++#'pointsize': '10pt', ++ ++# Additional stuff for the LaTeX preamble. ++#'preamble': '', ++ ++# Latex figure (float) alignment ++#'figure_align': 'htbp', ++} ++ ++# Grouping the document tree into LaTeX files. List of tuples ++# (source start file, target name, title, ++# author, documentclass [howto, manual, or own class]). ++latex_documents = [ ++ (master_doc, 'ld.tex', _short_description, author, 'manual'), ++] ++ ++# The name of an image file (relative to this directory) to place at the top of ++# the title page. ++#latex_logo = None ++ ++# For "manual" documents, if this is true, then toplevel headings are parts, ++# not chapters. ++#latex_use_parts = False ++ ++# If true, show page references after internal links. ++#latex_show_pagerefs = False ++ ++# If true, show URL addresses after external links. ++#latex_show_urls = False ++ ++# Documents to append as an appendix to all manuals. ++#latex_appendices = [] ++ ++# If false, no module index is generated. ++#latex_domain_indices = True ++ ++ ++# -- Options for manual page output --------------------------------------- ++ ++# One entry per manual page. List of tuples ++# (source start file, name, description, authors, manual section). ++man_pages = [ ++ (master_doc, 'ld', _short_description, [author], 1) ++] ++ ++# If true, show URL addresses after external links. ++#man_show_urls = False ++ ++ ++# -- Options for Texinfo output ------------------------------------------- ++ ++# Grouping the document tree into Texinfo files. List of tuples ++# (source start file, target name, title, author, ++# dir menu entry, description, category) ++texinfo_documents = [ ++ (master_doc, 'LinuxDistribution', _short_description, ++ author, 'LinuxDistribution', _short_description, ++ 'Miscellaneous'), ++] ++ ++# Documents to append as an appendix to all manuals. ++#texinfo_appendices = [] ++ ++# If false, no module index is generated. ++#texinfo_domain_indices = True ++ ++# How to display URL addresses: 'footnote', 'no', or 'inline'. ++#texinfo_show_urls = 'footnote' ++ ++# If true, do not generate a @detailmenu in the "Top" node's menu. ++#texinfo_no_detailmenu = False ++ ++ ++# -- Options for autodoc extension ---------------------------------------- ++# For documentation, see ++# http://www.sphinx-doc.org/en/stable/ext/autodoc.html ++ ++# Selects what content will be inserted into a class description. ++# The possible values are: ++# "class" - Only the class’ docstring is inserted. This is the default. ++# "both" - Both the class’ and the __init__ method’s docstring are ++# concatenated and inserted. ++# "init" - Only the __init__ method’s docstring is inserted. ++autoclass_content = "both" ++ ++# Selects if automatically documented members are sorted alphabetically ++# (value 'alphabetical'), by member type (value 'groupwise') or by source ++# order (value 'bysource'). The default is alphabetical. ++autodoc_member_order = "bysource" ++ ++# -- Options for intersphinx extension ------------------------------------ ++# For documentation, see ++# http://www.sphinx-doc.org/en/stable/ext/intersphinx.html ++ ++# Defines the prefixes for intersphinx links, and the targets they resolve ++# to. Example RST source for 'py' prefix: ++# :py:func:`platform.dist` ++intersphinx_mapping = { ++ 'py': ('https://docs.python.org/3.5', None) ++} ++ ++intersphinx_cache_limit = 5 +diff --git a/third_party/python/distro/docs/index.rst b/third_party/python/distro/docs/index.rst +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/docs/index.rst +@@ -0,0 +1,476 @@ ++ ++.. _distro official repo: https://github.com/nir0s/distro ++.. _distro issue tracker: https://github.com/nir0s/distro/issues ++.. _open issues on missing test data: https://github.com/nir0s/distro/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22> ++ ++ ++**distro** package (Linux Distribution) version |version| ++********************************************************* ++ ++Official distro repository: `distro official repo`_ ++ ++Overview and motivation ++======================= ++ ++.. automodule:: distro ++ ++If you want to jump into the API description right away, read about the ++`consolidated accessor functions`_. ++ ++Compatibility ++============= ++ ++The ``distro`` package is supported on Python 2.7, 3.4+ and PyPy, and on ++any Linux or *BSD distribution that provides one or more of the `data sources`_ ++used by this package. ++ ++This package is tested on Python 2.7, 3.4+ and PyPy, with test data that ++mimics the exact behavior of the data sources of ++`a number of Linux distributions `_. ++ ++If you want to add test data for more distributions, please ++create an issue in the `distro issue tracker`_ ++and provide the following information in the issue: ++ ++* The content of the `/etc/os-release` file, if any. ++* The file names and content of the `/etc/*release` and `/etc/*version` files, if any. ++* The output of the command: `lsb_release -a`, if available. ++* The file names and content of any other files you are aware of that provide ++ useful information about the distro. ++ ++There are already some `open issues on missing test data`_. ++ ++ ++Data sources ++============ ++ ++The ``distro`` package implements a robust and inclusive way of retrieving the ++information about a Linux distribution based on new standards and old methods, ++namely from these data sources: ++ ++* The `os-release file`_, if present. ++ ++* The `lsb_release command output`_, if the lsb_release command is available. ++ ++* The `distro release file`_, if present. ++ ++* The `uname command output`_, if present. ++ ++ ++Access to the information ++========================= ++ ++This package provides three ways to access the information about a Linux ++distribution: ++ ++* `Consolidated accessor functions`_ ++ ++ These are module-global functions that take into account all data sources in ++ a priority order, and that return information about the current Linux ++ distribution. ++ ++ These functions should be the normal way to access the information. ++ ++ The precedence of data sources is applied for each information item ++ separately. Therefore, it is possible that not all information items returned ++ by these functions come from the same data source. For example, on a ++ distribution that has an lsb_release command that returns the ++ "Distributor ID" field but not the "Codename" field, and that has a distro ++ release file that specifies a codename inside, the distro ID will come from ++ the lsb_release command (because it has higher precedence), and the codename ++ will come from the distro release file (because it is not provided by the ++ lsb_release command). ++ ++ Examples: :func:`distro.id` for retrieving ++ the distro ID, or :func:`ld.info` to get the machine-readable part of the ++ information in a more aggregated way, or :func:`distro.linux_distribution` with ++ an interface that is compatible to the original ++ :py:func:`platform.linux_distribution` function, supporting a subset of its ++ parameters. ++ ++* `Single source accessor functions`_ ++ ++ These are module-global functions that take into account a single data ++ source, and that return information about the current Linux distribution. ++ ++ They are useful for distributions that provide multiple inconsistent data ++ sources, or for retrieving information items that are not provided by the ++ consolidated accessor functions. ++ ++ Examples: :func:`distro.os_release_attr` for retrieving a single information ++ item from the os-release data source, or :func:`distro.lsb_release_info` for ++ retrieving all information items from the lsb_release command output data ++ source. ++ ++* `LinuxDistribution class`_ ++ ++ The :class:`distro.LinuxDistribution` class provides the main code of this ++ package. ++ ++ This package contains a private module-global :class:`distro.LinuxDistribution` ++ instance with default initialization arguments, that is used by the ++ consolidated and single source accessor functions. ++ ++ A user-defined instance of the :class:`distro.LinuxDistribution` class allows ++ specifying the path names of the os-release file and distro release file and ++ whether the lsb_release command should be used or not. That is useful for ++ example when the distribution information from a chrooted environment ++ is to be retrieved, or when a distro has multiple distro release files and ++ the default algorithm uses the wrong one. ++ ++ ++Consolidated accessor functions ++=============================== ++ ++This section describes the consolidated accessor functions. ++See `access to the information`_ for a discussion of the different kinds of ++accessor functions. ++ ++.. autofunction:: distro.linux_distribution ++.. autofunction:: distro.id ++.. autofunction:: distro.name ++.. autofunction:: distro.version ++.. autofunction:: distro.version_parts ++.. autofunction:: distro.major_version ++.. autofunction:: distro.minor_version ++.. autofunction:: distro.build_number ++.. autofunction:: distro.like ++.. autofunction:: distro.codename ++.. autofunction:: distro.info ++ ++Single source accessor functions ++================================ ++ ++This section describes the single source accessor functions. ++See `access to the information`_ for a discussion of the different kinds of ++accessor functions. ++ ++.. autofunction:: distro.os_release_info ++.. autofunction:: distro.lsb_release_info ++.. autofunction:: distro.distro_release_info ++.. autofunction:: distro.os_release_attr ++.. autofunction:: distro.lsb_release_attr ++.. autofunction:: distro.distro_release_attr ++ ++LinuxDistribution class ++======================= ++ ++This section describes the access via the :class:`distro.LinuxDistribution` class. ++See `access to the information`_ for a discussion of the different kinds of ++accessor functions. ++ ++.. autoclass:: distro.LinuxDistribution ++ :members: ++ :undoc-members: ++ ++Normalization tables ++==================== ++ ++These translation tables are used to normalize the parsed distro ID values ++into reliable IDs. See :func:`distro.id` for details. ++ ++They are documented in order to show for which distros a normalization is ++currently defined. ++ ++As a quick fix, these tables can also be extended by the user by appending new ++entries, should the need arise. If you have a need to get these tables ++extended, please make an according request in the `distro issue tracker`_. ++ ++.. autodata:: distro.NORMALIZED_OS_ID ++.. autodata:: distro.NORMALIZED_LSB_ID ++.. autodata:: distro.NORMALIZED_DISTRO_ID ++ ++Os-release file ++=============== ++ ++The os-release file is looked up using the path name ``/etc/os-release``. Its ++optional additional location ``/usr/lib/os-release`` is ignored. ++ ++The os-release file is expected to be encoded in UTF-8. ++ ++It is parsed using the standard Python :py:mod:`shlex` package, which treats it ++like a shell script. ++ ++The attribute names found in the file are translated to lower case and then ++become the keys of the information items from the os-release file data source. ++These keys can be used to retrieve single items with the ++:func:`distro.os_release_attr` function, and they are also used as keys in the ++dictionary returned by :func:`distro.os_release_info`. ++ ++The attribute values found in the file are processed using shell rules (e.g. ++for whitespace, escaping, and quoting) before they become the values of the ++information items from the os-release file data source. ++ ++If the attribute "VERSION" is found in the file, the distro codename is ++extracted from its value if it can be found there. If a codename is found, it ++becomes an additional information item with key "codename". ++ ++See the `os-release man page ++`_ ++for a list of possible attributes in the file. ++ ++**Examples:** ++ ++1. The following os-release file content: ++ ++ .. sourcecode:: shell ++ ++ NAME='Ubuntu' ++ VERSION="14.04.3 LTS, Trusty Tahr" ++ ID=ubuntu ++ ID_LIKE=debian ++ PRETTY_NAME="Ubuntu 14.04.3 LTS" ++ VERSION_ID="14.04" ++ HOME_URL="http://www.ubuntu.com/" ++ SUPPORT_URL="http://help.ubuntu.com/" ++ BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" ++ ++ results in these information items: ++ ++ =============================== ========================================== ++ Key Value ++ =============================== ========================================== ++ name "Ubuntu" ++ version "14.04.3 LTS, Trusty Tahr" ++ id "ubuntu" ++ id_like "debian" ++ pretty_name "Ubuntu 14.04.3 LTS" ++ version_id "14.04" ++ home_url "http://www.ubuntu.com/" ++ support_url "http://help.ubuntu.com/" ++ bug_report_url "http://bugs.launchpad.net/ubuntu/" ++ codename "Trusty Tahr" ++ =============================== ========================================== ++ ++2. The following os-release file content: ++ ++ .. sourcecode:: shell ++ ++ NAME="Red Hat Enterprise Linux Server" ++ VERSION="7.0 (Maipo)" ++ ID="rhel" ++ ID_LIKE="fedora" ++ VERSION_ID="7.0" ++ PRETTY_NAME="Red Hat Enterprise Linux Server 7.0 (Maipo)" ++ ANSI_COLOR="0;31" ++ CPE_NAME="cpe:/o:redhat:enterprise_linux:7.0:GA:server" ++ HOME_URL="https://www.redhat.com/" ++ BUG_REPORT_URL="https://bugzilla.redhat.com/" ++ ++ REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 7" ++ REDHAT_BUGZILLA_PRODUCT_VERSION=7.0 ++ REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux" ++ REDHAT_SUPPORT_PRODUCT_VERSION=7.0 ++ ++ results in these information items: ++ ++ =============================== ========================================== ++ Key Value ++ =============================== ========================================== ++ name "Red Hat Enterprise Linux Server" ++ version "7.0 (Maipo)" ++ id "rhel" ++ id_like "fedora" ++ version_id "7.0" ++ pretty_name "Red Hat Enterprise Linux Server 7.0 (Maipo)" ++ ansi_color "0;31" ++ cpe_name "cpe:/o:redhat:enterprise_linux:7.0:GA:server" ++ home_url "https://www.redhat.com/" ++ bug_report_url "https://bugzilla.redhat.com/" ++ redhat_bugzilla_product "Red Hat Enterprise Linux 7" ++ redhat_bugzilla_product_version "7.0" ++ redhat_support_product "Red Hat Enterprise Linux" ++ redhat_support_product_version "7.0" ++ codename "Maipo" ++ =============================== ========================================== ++ ++Lsb_release command output ++========================== ++ ++The lsb_release command is expected to be in the PATH, and is invoked as ++follows: ++ ++.. sourcecode:: shell ++ ++ lsb_release -a ++ ++The command output is expected to be encoded in UTF-8. ++ ++Only lines in the command output with the following format will be used: ++ ++ ``: `` ++ ++Where: ++ ++* ```` is the name of the attribute, and ++* ```` is the attribute value. ++ ++The attribute names are stripped from surrounding blanks, any remaining blanks ++are translated to underscores, they are translated to lower case, and then ++become the keys of the information items from the lsb_release command output ++data source. ++ ++The attribute values are stripped from surrounding blanks, and then become the ++values of the information items from the lsb_release command output data ++source. ++ ++See the `lsb_release man page ++`_ ++for a description of standard attributes returned by the lsb_release command. ++ ++**Examples:** ++ ++1. The following lsb_release command output: ++ ++ .. sourcecode:: text ++ ++ No LSB modules are available. ++ Distributor ID: Ubuntu ++ Description: Ubuntu 14.04.3 LTS ++ Release: 14.04 ++ Codename: trusty ++ ++ results in these information items: ++ ++ =============================== ========================================== ++ Key Value ++ =============================== ========================================== ++ distributor_id "Ubuntu" ++ description "Ubuntu 14.04.3 LTS" ++ release "14.04" ++ codename "trusty" ++ =============================== ========================================== ++ ++2. The following lsb_release command output: ++ ++ .. sourcecode:: text ++ ++ LSB Version: n/a ++ Distributor ID: SUSE LINUX ++ Description: SUSE Linux Enterprise Server 12 SP1 ++ Release: 12.1 ++ Codename: n/a ++ ++ results in these information items: ++ ++ =============================== ========================================== ++ Key Value ++ =============================== ========================================== ++ lsb_version "n/a" ++ distributor_id "SUSE LINUX" ++ description "SUSE Linux Enterprise Server 12 SP1" ++ release "12.1" ++ codename "n/a" ++ =============================== ========================================== ++ ++Distro release file ++=================== ++ ++Unless specified with a particular path name when using the ++:class:`distro.LinuxDistribution` class, the distro release file is found by using ++the first match in the alphabetically sorted list of the files matching the ++following path name patterns: ++ ++* ``/etc/*-release`` ++* ``/etc/*_release`` ++* ``/etc/*-version`` ++* ``/etc/*_version`` ++ ++where the following special path names are excluded: ++ ++* ``/etc/debian_version`` ++* ``/etc/system-release`` ++* ``/etc/os-release`` ++ ++and where the first line within the file has the expected format. ++ ++The algorithm to sort the files alphabetically is far from perfect, but the ++distro release file has the least priority as a data source, and it is expected ++that distributions provide one of the other data sources. ++ ++The distro release file is expected to be encoded in UTF-8. ++ ++Only its first line is used, and it is expected to have the following format: ++ ++ `` [[[release] ] ()]`` ++ ++Where: ++ ++* square brackets indicate optionality, ++* ```` is the distro name, ++* ```` is the distro version, and ++* ```` is the distro codename. ++ ++The following information items can be found in a distro release file ++(shown with their keys and data types): ++ ++* ``id`` (string): Distro ID, taken from the first part of the file name ++ before the hyphen (``-``) or underscore (``_``). ++ ++ Note that the distro ID is not normalized or translated to lower case at this ++ point; this happens only for the result of the :func:`distro.id` function. ++ ++* ``name`` (string): Distro name, as found in the first line of the file. ++ ++* ``version_id`` (string): Distro version, as found in the first line of the ++ file. If not found, this information item will not exist. ++ ++* ``codename`` (string): Distro codename, as found in the first line of the ++ file. If not found, this information item will not exist. ++ ++ Note that the string in the codename field is not always really a ++ codename. For example, openSUSE returns "x86_64". ++ ++**Examples:** ++ ++1. The following distro release file ``/etc/centos-release``: ++ ++ .. sourcecode:: text ++ ++ CentOS Linux release 7.1.1503 (Core) ++ ++ results in these information items: ++ ++ =============================== ========================================== ++ Key Value ++ =============================== ========================================== ++ id "centos" ++ name "CentOS Linux" ++ version_id "7.1.1503" ++ codename "Core" ++ =============================== ========================================== ++ ++2. The following distro release file ``/etc/oracle-release``: ++ ++ .. sourcecode:: text ++ ++ Oracle Linux Server release 7.1 ++ ++ results in these information items: ++ ++ =============================== ========================================== ++ Key Value ++ =============================== ========================================== ++ id "oracle" ++ name "Oracle Linux Server" ++ version_id "7.1" ++ =============================== ========================================== ++ ++3. The following distro release file ``/etc/SuSE-release``: ++ ++ .. sourcecode:: text ++ ++ openSUSE 42.1 (x86_64) ++ ++ results in these information items: ++ ++ =============================== ========================================== ++ Key Value ++ =============================== ========================================== ++ id "SuSE" ++ name "openSUSE" ++ version_id "42.1" ++ codename "x86_64" ++ =============================== ========================================== ++ +diff --git a/third_party/python/distro/query_local_distro.py b/third_party/python/distro/query_local_distro.py +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/query_local_distro.py +@@ -0,0 +1,45 @@ ++#!/usr/bin/env python ++# Copyright 2015,2016 Nir Cohen ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++from __future__ import print_function ++ ++from pprint import pformat ++ ++import distro ++ ++ ++def pprint(obj): ++ for line in pformat(obj).split('\n'): ++ print(4 * ' ' + line) ++ ++ ++print('os_release_info:') ++pprint(distro.os_release_info()) ++print('lsb_release_info:') ++pprint(distro.lsb_release_info()) ++print('distro_release_info:') ++pprint(distro.distro_release_info()) ++print('id: {0}'.format(distro.id())) ++print('name: {0}'.format(distro.name())) ++print('name_pretty: {0}'.format(distro.name(True))) ++print('version: {0}'.format(distro.version())) ++print('version_pretty: {0}'.format(distro.version(True))) ++print('like: {0}'.format(distro.like())) ++print('codename: {0}'.format(distro.codename())) ++print('linux_distribution_full: {0}'.format(distro.linux_distribution())) ++print('linux_distribution: {0}'.format(distro.linux_distribution(False))) ++print('major_version: {0}'.format(distro.major_version())) ++print('minor_version: {0}'.format(distro.minor_version())) ++print('build_number: {0}'.format(distro.build_number())) +diff --git a/third_party/python/distro/setup.cfg b/third_party/python/distro/setup.cfg +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/setup.cfg +@@ -0,0 +1,10 @@ ++[bdist_wheel] ++universal = 1 ++ ++[metadata] ++license_file = LICENSE ++ ++[egg_info] ++tag_build = ++tag_date = 0 ++ +diff --git a/third_party/python/distro/setup.py b/third_party/python/distro/setup.py +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/setup.py +@@ -0,0 +1,67 @@ ++# Copyright 2015,2016 Nir Cohen ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++import os ++import codecs ++from setuptools import setup ++ ++# The following version is parsed by other parts of this package. ++# Don't change the format of the line, or the variable name. ++package_version = "1.4.0" ++ ++here = os.path.abspath(os.path.dirname(__file__)) ++ ++ ++def read(*parts): ++ # intentionally *not* adding an encoding option to open ++ return codecs.open(os.path.join(here, *parts), 'r').read() ++ ++ ++setup( ++ name='distro', ++ version=package_version, ++ url='https://github.com/nir0s/distro', ++ author='Nir Cohen', ++ author_email='nir36g@gmail.com', ++ license='Apache License, Version 2.0', ++ platforms='All', ++ description='Distro - an OS platform information API', ++ long_description=read('README.md'), ++ long_description_content_type='text/markdown', ++ py_modules=['distro'], ++ entry_points={ ++ 'console_scripts': [ ++ 'distro = distro:main', ++ ] ++ }, ++ classifiers=[ ++ 'Development Status :: 5 - Production/Stable', ++ 'Intended Audience :: Developers', ++ 'Intended Audience :: System Administrators', ++ 'License :: OSI Approved :: Apache Software License', ++ 'Operating System :: POSIX :: Linux', ++ 'Operating System :: POSIX :: BSD', ++ 'Operating System :: POSIX :: BSD :: FreeBSD', ++ 'Operating System :: POSIX :: BSD :: NetBSD', ++ 'Operating System :: POSIX :: BSD :: OpenBSD', ++ 'Programming Language :: Python :: 2', ++ 'Programming Language :: Python :: 2.7', ++ 'Programming Language :: Python :: 3', ++ 'Programming Language :: Python :: 3.4', ++ 'Programming Language :: Python :: 3.5', ++ 'Programming Language :: Python :: 3.6', ++ 'Topic :: Software Development :: Libraries :: Python Modules', ++ 'Topic :: System :: Operating System', ++ ] ++) +diff --git a/third_party/python/distro/tests/__init__.py b/third_party/python/distro/tests/__init__.py +new file mode 100644 +diff --git a/third_party/python/distro/tests/resources/distros/__shared__/bin/lsb_release b/third_party/python/distro/tests/resources/distros/__shared__/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/__shared__/bin/lsb_release +@@ -0,0 +1,43 @@ ++#!/bin/bash ++# ++# lsb_release command for testing the ld module. ++# Only the -a option is supported. ++# ++# This version of the lsb_release command reads an lsb-release file. ++# ++# The lsb-release file has the usual format, e.g.: ++# DISTRIB_ID=Ubuntu ++# DISTRIB_RELEASE=14.04 ++# DISTRIB_CODENAME=trusty ++# DISTRIB_DESCRIPTION="Ubuntu 14.04.3 LTS" ++# Where each line is optional. If a line is missing, the default value ++# will be the empty string. ++# ++ ++if [[ "$@" != "-a" ]]; then ++ echo "Usage: lsb_release -a" ++ exit 2 ++fi ++ ++# Because the PATH is set to just this directory, we cannot use 'dirname' ++# or other external programs, but need to use built-in abilities of bash. ++LSB_FILE="${0%/*}/../etc/lsb-release" ++ ++if [[ ! -f $LSB_FILE ]]; then ++ echo "Error: LSB release file does not exist: $LSB_FILE" ++ exit 1 ++fi ++ ++source $LSB_FILE ++ ++if [[ -n $LSB_VERSION ]]; then ++ echo "LSB Version: $LSB_VERSION" ++else ++ echo "No LSB modules are available." ++fi ++echo "Distributor ID: ${DISTRIB_ID:-}" ++echo "Description: ${DISTRIB_DESCRIPTION:-}" ++echo "Release: ${DISTRIB_RELEASE:-}" ++echo "Codename: ${DISTRIB_CODENAME:-}" ++ ++exit 0 +diff --git a/third_party/python/distro/tests/resources/distros/amazon2014/etc/system-release b/third_party/python/distro/tests/resources/distros/amazon2014/etc/system-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/amazon2014/etc/system-release +@@ -0,0 +1,1 @@ ++Amazon Linux AMI release 2014.03 +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/amazon2016/etc/os-release b/third_party/python/distro/tests/resources/distros/amazon2016/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/amazon2016/etc/os-release +@@ -0,0 +1,9 @@ ++NAME="Amazon Linux AMI" ++VERSION="2016.03" ++ID="amzn" ++ID_LIKE="rhel fedora" ++VERSION_ID="2016.03" ++PRETTY_NAME="Amazon Linux AMI 2016.03" ++ANSI_COLOR="0;33" ++CPE_NAME="cpe:/o:amazon:linux:2016.03:ga" ++HOME_URL="http://aws.amazon.com/amazon-linux-ami/" +diff --git a/third_party/python/distro/tests/resources/distros/amazon2016/etc/system-release b/third_party/python/distro/tests/resources/distros/amazon2016/etc/system-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/amazon2016/etc/system-release +@@ -0,0 +1,1 @@ ++Amazon Linux AMI release 2016.03 +diff --git a/third_party/python/distro/tests/resources/distros/arch/etc/arch-release b/third_party/python/distro/tests/resources/distros/arch/etc/arch-release +new file mode 100644 +diff --git a/third_party/python/distro/tests/resources/distros/arch/etc/os-release b/third_party/python/distro/tests/resources/distros/arch/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/arch/etc/os-release +@@ -0,0 +1,7 @@ ++NAME="Arch Linux" ++ID=arch ++PRETTY_NAME="Arch Linux" ++ANSI_COLOR="0;36" ++HOME_URL="https://www.archlinux.org/" ++SUPPORT_URL="https://bbs.archlinux.org/" ++BUG_REPORT_URL="https://bugs.archlinux.org/" +diff --git a/third_party/python/distro/tests/resources/distros/arch/usr/lib/os-release b/third_party/python/distro/tests/resources/distros/arch/usr/lib/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/arch/usr/lib/os-release +@@ -0,0 +1,7 @@ ++NAME="Arch Linux" ++ID=arch ++PRETTY_NAME="Arch Linux" ++ANSI_COLOR="0;36" ++HOME_URL="https://www.archlinux.org/" ++SUPPORT_URL="https://bbs.archlinux.org/" ++BUG_REPORT_URL="https://bugs.archlinux.org/" +diff --git a/third_party/python/distro/tests/resources/distros/centos5/etc/centos-release b/third_party/python/distro/tests/resources/distros/centos5/etc/centos-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/centos5/etc/centos-release +@@ -0,0 +1,1 @@ ++CentOS release 5.11 (Final) +diff --git a/third_party/python/distro/tests/resources/distros/centos5/etc/redhat-release b/third_party/python/distro/tests/resources/distros/centos5/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/centos5/etc/redhat-release +@@ -0,0 +1,1 @@ ++CentOS release 5.11 (Final) +diff --git a/third_party/python/distro/tests/resources/distros/centos5/etc/system-release b/third_party/python/distro/tests/resources/distros/centos5/etc/system-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/centos5/etc/system-release +@@ -0,0 +1,1 @@ ++CentOS release 5.11 (Final) +diff --git a/third_party/python/distro/tests/resources/distros/centos7/etc/centos-release b/third_party/python/distro/tests/resources/distros/centos7/etc/centos-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/centos7/etc/centos-release +@@ -0,0 +1,1 @@ ++CentOS Linux release 7.1.1503 (Core) +diff --git a/third_party/python/distro/tests/resources/distros/centos7/etc/os-release b/third_party/python/distro/tests/resources/distros/centos7/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/centos7/etc/os-release +@@ -0,0 +1,16 @@ ++NAME="CentOS Linux" ++VERSION="7 (Core)" ++ID="centos" ++ID_LIKE="rhel fedora" ++VERSION_ID="7" ++PRETTY_NAME="CentOS Linux 7 (Core)" ++ANSI_COLOR="0;31" ++CPE_NAME="cpe:/o:centos:centos:7" ++HOME_URL="https://www.centos.org/" ++BUG_REPORT_URL="https://bugs.centos.org/" ++ ++CENTOS_MANTISBT_PROJECT="CentOS-7" ++CENTOS_MANTISBT_PROJECT_VERSION="7" ++REDHAT_SUPPORT_PRODUCT="centos" ++REDHAT_SUPPORT_PRODUCT_VERSION="7" ++ +diff --git a/third_party/python/distro/tests/resources/distros/centos7/etc/redhat-release b/third_party/python/distro/tests/resources/distros/centos7/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/centos7/etc/redhat-release +@@ -0,0 +1,1 @@ ++CentOS Linux release 7.1.1503 (Core) +diff --git a/third_party/python/distro/tests/resources/distros/centos7/etc/system-release b/third_party/python/distro/tests/resources/distros/centos7/etc/system-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/centos7/etc/system-release +@@ -0,0 +1,1 @@ ++CentOS Linux release 7.1.1503 (Core) +diff --git a/third_party/python/distro/tests/resources/distros/cloudlinux5/etc/redhat-release b/third_party/python/distro/tests/resources/distros/cloudlinux5/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/cloudlinux5/etc/redhat-release +@@ -0,0 +1,1 @@ ++CloudLinux Server release 5.11 (Vladislav Volkov) +diff --git a/third_party/python/distro/tests/resources/distros/cloudlinux6/etc/redhat-release b/third_party/python/distro/tests/resources/distros/cloudlinux6/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/cloudlinux6/etc/redhat-release +@@ -0,0 +1,1 @@ ++CloudLinux Server release 6.8 (Oleg Makarov) +diff --git a/third_party/python/distro/tests/resources/distros/cloudlinux7/etc/os-release b/third_party/python/distro/tests/resources/distros/cloudlinux7/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/cloudlinux7/etc/os-release +@@ -0,0 +1,10 @@ ++NAME="CloudLinux" ++VERSION="7.3 (Yury Malyshev)" ++ID="cloudlinux" ++ID_LIKE="rhel fedora centos" ++VERSION_ID="7.3" ++PRETTY_NAME="CloudLinux 7.3 (Yury Malyshev)" ++ANSI_COLOR="0:31" ++CPE_NAME="cpe:/o:cloudlinux:cloudlinux:7.3:GA:server" ++HOME_URL="https://www.cloudlinux.com/" ++BUG_REPORT_URL="https://helpdesk.cloudlinux.com/" +diff --git a/third_party/python/distro/tests/resources/distros/cloudlinux7/etc/redhat-release b/third_party/python/distro/tests/resources/distros/cloudlinux7/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/cloudlinux7/etc/redhat-release +@@ -0,0 +1,1 @@ ++CloudLinux release 7.3 (Yury Malyshev) +diff --git a/third_party/python/distro/tests/resources/distros/coreos/etc/oem-release b/third_party/python/distro/tests/resources/distros/coreos/etc/oem-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/coreos/etc/oem-release +@@ -0,0 +1,5 @@ ++ID=digitalocean ++VERSION_ID=0.0.4 ++NAME="DigitalOcean" ++HOME_URL="https://www.digitalocean.com/" ++BUG_REPORT_URL="https://github.com/coreos/bugs/issues" +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/coreos/etc/os-release b/third_party/python/distro/tests/resources/distros/coreos/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/coreos/etc/os-release +@@ -0,0 +1,9 @@ ++NAME=CoreOS ++ID=coreos ++VERSION=899.15.0 ++VERSION_ID=899.15.0 ++BUILD_ID=2016-04-05-1035 ++PRETTY_NAME="CoreOS 899.15.0" ++ANSI_COLOR="1;32" ++HOME_URL="https://coreos.com/" ++BUG_REPORT_URL="https://github.com/coreos/bugs/issues" +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/debian8/bin/lsb_release b/third_party/python/distro/tests/resources/distros/debian8/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/debian8/bin/lsb_release +@@ -0,0 +1,21 @@ ++#!/bin/bash ++# ++# lsb_release command for testing the ld module. ++# Only the -a option is supported. ++# ++# This version of the lsb_release command works without a corresponding ++# etc/lsb-release file. ++# ++ ++if [[ "$@" != "-a" ]]; then ++ echo "Usage: lsb_release -a" ++ exit 2 ++fi ++ ++echo "No LSB modules are available." ++echo "Distributor ID: Debian" ++echo "Description: Debian GNU/Linux 8.2 (jessie)" ++echo "Release: 8.2" ++echo "Codename: jessie" ++ ++exit 0 +diff --git a/third_party/python/distro/tests/resources/distros/debian8/etc/debian_version b/third_party/python/distro/tests/resources/distros/debian8/etc/debian_version +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/debian8/etc/debian_version +@@ -0,0 +1,1 @@ ++8.2 +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/debian8/etc/os-release b/third_party/python/distro/tests/resources/distros/debian8/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/debian8/etc/os-release +@@ -0,0 +1,8 @@ ++PRETTY_NAME="Debian GNU/Linux 8 (jessie)" ++NAME="Debian GNU/Linux" ++VERSION_ID="8" ++VERSION="8 (jessie)" ++ID=debian ++HOME_URL="http://www.debian.org/" ++SUPPORT_URL="http://www.debian.org/support/" ++BUG_REPORT_URL="https://bugs.debian.org/" +diff --git a/third_party/python/distro/tests/resources/distros/exherbo/etc/os-release b/third_party/python/distro/tests/resources/distros/exherbo/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/exherbo/etc/os-release +@@ -0,0 +1,7 @@ ++NAME="Exherbo" ++PRETTY_NAME="Exherbo Linux" ++ID="exherbo" ++ANSI_COLOR="0;32" ++HOME_URL="https://www.exherbo.org/" ++SUPPORT_URL="irc://irc.freenode.net/#exherbo" ++BUG_REPORT_URL="https://bugs.exherbo.org/" +diff --git a/third_party/python/distro/tests/resources/distros/fedora19/etc/fedora-release b/third_party/python/distro/tests/resources/distros/fedora19/etc/fedora-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora19/etc/fedora-release +@@ -0,0 +1,1 @@ ++Fedora release 19 (SchrΓΆdinger’s Cat) +diff --git a/third_party/python/distro/tests/resources/distros/fedora19/etc/issue b/third_party/python/distro/tests/resources/distros/fedora19/etc/issue +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora19/etc/issue +@@ -0,0 +1,3 @@ ++Fedora release 19 (SchrΓΆdinger’s Cat) ++Kernel \r on an \m (\l) ++ +diff --git a/third_party/python/distro/tests/resources/distros/fedora19/etc/issue.net b/third_party/python/distro/tests/resources/distros/fedora19/etc/issue.net +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora19/etc/issue.net +@@ -0,0 +1,2 @@ ++Fedora release 19 (SchrΓΆdinger’s Cat) ++Kernel \r on an \m (\l) +diff --git a/third_party/python/distro/tests/resources/distros/fedora19/etc/os-release b/third_party/python/distro/tests/resources/distros/fedora19/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora19/etc/os-release +@@ -0,0 +1,7 @@ ++NAME=Fedora ++VERSION="19 (SchrΓΆdinger’s Cat)" ++ID=fedora ++VERSION_ID=19 ++PRETTY_NAME="Fedora 19 (SchrΓΆdinger’s Cat)" ++ANSI_COLOR="0;34" ++CPE_NAME="cpe:/o:fedoraproject:fedora:19" +diff --git a/third_party/python/distro/tests/resources/distros/fedora19/etc/redhat-release b/third_party/python/distro/tests/resources/distros/fedora19/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora19/etc/redhat-release +@@ -0,0 +1,1 @@ ++Fedora release 19 (SchrΓΆdinger’s Cat) +diff --git a/third_party/python/distro/tests/resources/distros/fedora19/etc/system-release b/third_party/python/distro/tests/resources/distros/fedora19/etc/system-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora19/etc/system-release +@@ -0,0 +1,1 @@ ++Fedora release 19 (SchrΓΆdinger’s Cat) +diff --git a/third_party/python/distro/tests/resources/distros/fedora19/etc/system-release-cpe b/third_party/python/distro/tests/resources/distros/fedora19/etc/system-release-cpe +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora19/etc/system-release-cpe +@@ -0,0 +1,1 @@ ++cpe:/o:fedoraproject:fedora:19 +diff --git a/third_party/python/distro/tests/resources/distros/fedora23/etc/fedora-release b/third_party/python/distro/tests/resources/distros/fedora23/etc/fedora-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora23/etc/fedora-release +@@ -0,0 +1,1 @@ ++Fedora release 23 (Twenty Three) +diff --git a/third_party/python/distro/tests/resources/distros/fedora23/etc/os-release b/third_party/python/distro/tests/resources/distros/fedora23/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora23/etc/os-release +@@ -0,0 +1,14 @@ ++NAME=Fedora ++VERSION="23 (Twenty Three)" ++ID=fedora ++VERSION_ID=23 ++PRETTY_NAME="Fedora 23 (Twenty Three)" ++ANSI_COLOR="0;34" ++CPE_NAME="cpe:/o:fedoraproject:fedora:23" ++HOME_URL="https://fedoraproject.org/" ++BUG_REPORT_URL="https://bugzilla.redhat.com/" ++REDHAT_BUGZILLA_PRODUCT="Fedora" ++REDHAT_BUGZILLA_PRODUCT_VERSION=23 ++REDHAT_SUPPORT_PRODUCT="Fedora" ++REDHAT_SUPPORT_PRODUCT_VERSION=23 ++PRIVACY_POLICY_URL=https://fedoraproject.org/wiki/Legal:PrivacyPolicy +diff --git a/third_party/python/distro/tests/resources/distros/fedora23/etc/redhat-release b/third_party/python/distro/tests/resources/distros/fedora23/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora23/etc/redhat-release +@@ -0,0 +1,1 @@ ++Fedora release 23 (Twenty Three) +diff --git a/third_party/python/distro/tests/resources/distros/fedora23/etc/system-release b/third_party/python/distro/tests/resources/distros/fedora23/etc/system-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora23/etc/system-release +@@ -0,0 +1,1 @@ ++Fedora release 23 (Twenty Three) +diff --git a/third_party/python/distro/tests/resources/distros/fedora23/usr/lib/os-release b/third_party/python/distro/tests/resources/distros/fedora23/usr/lib/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora23/usr/lib/os-release +@@ -0,0 +1,14 @@ ++NAME=Fedora ++VERSION="23 (Twenty Three)" ++ID=fedora ++VERSION_ID=23 ++PRETTY_NAME="Fedora 23 (Twenty Three)" ++ANSI_COLOR="0;34" ++CPE_NAME="cpe:/o:fedoraproject:fedora:23" ++HOME_URL="https://fedoraproject.org/" ++BUG_REPORT_URL="https://bugzilla.redhat.com/" ++REDHAT_BUGZILLA_PRODUCT="Fedora" ++REDHAT_BUGZILLA_PRODUCT_VERSION=23 ++REDHAT_SUPPORT_PRODUCT="Fedora" ++REDHAT_SUPPORT_PRODUCT_VERSION=23 ++PRIVACY_POLICY_URL=https://fedoraproject.org/wiki/Legal:PrivacyPolicy +diff --git a/third_party/python/distro/tests/resources/distros/fedora30/etc/fedora-release b/third_party/python/distro/tests/resources/distros/fedora30/etc/fedora-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora30/etc/fedora-release +@@ -0,0 +1,1 @@ ++Fedora release 30 (Thirty) +diff --git a/third_party/python/distro/tests/resources/distros/fedora30/etc/os-release b/third_party/python/distro/tests/resources/distros/fedora30/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora30/etc/os-release +@@ -0,0 +1,19 @@ ++NAME=Fedora ++VERSION="30 (Thirty)" ++ID=fedora ++VERSION_ID=30 ++VERSION_CODENAME="" ++PLATFORM_ID="platform:f30" ++PRETTY_NAME="Fedora 30 (Thirty)" ++ANSI_COLOR="0;34" ++LOGO=fedora-logo-icon ++CPE_NAME="cpe:/o:fedoraproject:fedora:30" ++HOME_URL="https://fedoraproject.org/" ++DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/30/system-administrators-guide/" ++SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help" ++BUG_REPORT_URL="https://bugzilla.redhat.com/" ++REDHAT_BUGZILLA_PRODUCT="Fedora" ++REDHAT_BUGZILLA_PRODUCT_VERSION=30 ++REDHAT_SUPPORT_PRODUCT="Fedora" ++REDHAT_SUPPORT_PRODUCT_VERSION=30 ++PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy" +diff --git a/third_party/python/distro/tests/resources/distros/fedora30/etc/redhat-release b/third_party/python/distro/tests/resources/distros/fedora30/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora30/etc/redhat-release +@@ -0,0 +1,1 @@ ++Fedora release 30 (Thirty) +diff --git a/third_party/python/distro/tests/resources/distros/fedora30/etc/system-release b/third_party/python/distro/tests/resources/distros/fedora30/etc/system-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora30/etc/system-release +@@ -0,0 +1,1 @@ ++Fedora release 30 (Thirty) +diff --git a/third_party/python/distro/tests/resources/distros/fedora30/usr/lib/os-release b/third_party/python/distro/tests/resources/distros/fedora30/usr/lib/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/fedora30/usr/lib/os-release +@@ -0,0 +1,19 @@ ++NAME=Fedora ++VERSION="30 (Thirty)" ++ID=fedora ++VERSION_ID=30 ++VERSION_CODENAME="" ++PLATFORM_ID="platform:f30" ++PRETTY_NAME="Fedora 30 (Thirty)" ++ANSI_COLOR="0;34" ++LOGO=fedora-logo-icon ++CPE_NAME="cpe:/o:fedoraproject:fedora:30" ++HOME_URL="https://fedoraproject.org/" ++DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/30/system-administrators-guide/" ++SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help" ++BUG_REPORT_URL="https://bugzilla.redhat.com/" ++REDHAT_BUGZILLA_PRODUCT="Fedora" ++REDHAT_BUGZILLA_PRODUCT_VERSION=30 ++REDHAT_SUPPORT_PRODUCT="Fedora" ++REDHAT_SUPPORT_PRODUCT_VERSION=30 ++PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy" +diff --git a/third_party/python/distro/tests/resources/distros/freebsd111/bin/uname b/third_party/python/distro/tests/resources/distros/freebsd111/bin/uname +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/freebsd111/bin/uname +@@ -0,0 +1,4 @@ ++#!/bin/sh ++ ++echo "FreeBSD 11.1-RELEASE" ++ +diff --git a/third_party/python/distro/tests/resources/distros/gentoo/etc/gentoo-release b/third_party/python/distro/tests/resources/distros/gentoo/etc/gentoo-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/gentoo/etc/gentoo-release +@@ -0,0 +1,1 @@ ++Gentoo Base System release 2.2 +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/gentoo/etc/os-release b/third_party/python/distro/tests/resources/distros/gentoo/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/gentoo/etc/os-release +@@ -0,0 +1,7 @@ ++NAME=Gentoo ++ID=gentoo ++PRETTY_NAME="Gentoo/Linux" ++ANSI_COLOR="1;32" ++HOME_URL="http://www.gentoo.org/" ++SUPPORT_URL="http://www.gentoo.org/main/en/support.xml" ++BUG_REPORT_URL="https://bugs.gentoo.org/" +diff --git a/third_party/python/distro/tests/resources/distros/kali/etc/os-release b/third_party/python/distro/tests/resources/distros/kali/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/kali/etc/os-release +@@ -0,0 +1,10 @@ ++PRETTY_NAME="Kali GNU/Linux Rolling" ++NAME="Kali GNU/Linux" ++ID=kali ++VERSION="2017.1" ++VERSION_ID="2017.1" ++ID_LIKE=debian ++ANSI_COLOR="1;31" ++HOME_URL="http://www.kali.org/" ++SUPPORT_URL="http://forums.kali.org/" ++BUG_REPORT_URL="http://bugs.kali.org/" +diff --git a/third_party/python/distro/tests/resources/distros/kvmibm1/bin/lsb_release b/third_party/python/distro/tests/resources/distros/kvmibm1/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/kvmibm1/bin/lsb_release +@@ -0,0 +1,21 @@ ++#!/bin/bash ++# ++# lsb_release command for testing the ld module. ++# Only the -a option is supported. ++# ++# This version of the lsb_release command works without a corresponding ++# etc/lsb-release file. ++# ++ ++if [[ "$@" != "-a" ]]; then ++ echo "Usage: lsb_release -a" ++ exit 2 ++fi ++ ++echo "LSB Version: :core-4.1-noarch:core-4.1-s390x" ++echo "Distributor ID: kvmibm" ++echo "Description: KVM for IBM z Systems release 1.1.1 (Z) " ++echo "Release: 1.1.1" ++echo "Codename: Z" ++ ++exit 0 +diff --git a/third_party/python/distro/tests/resources/distros/kvmibm1/etc/base-release b/third_party/python/distro/tests/resources/distros/kvmibm1/etc/base-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/kvmibm1/etc/base-release +@@ -0,0 +1,1 @@ ++KVM for IBM z Systems release 1.1.1 (Z) +diff --git a/third_party/python/distro/tests/resources/distros/kvmibm1/etc/os-release b/third_party/python/distro/tests/resources/distros/kvmibm1/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/kvmibm1/etc/os-release +@@ -0,0 +1,9 @@ ++NAME="KVM for IBM z Systems" ++VERSION="1.1.1 (Z)" ++ID="kvmibm" ++ID_LIKE="rhel fedora" ++VERSION_ID="1.1.1" ++PRETTY_NAME="KVM for IBM z Systems 1.1.1 (Z)" ++ANSI_COLOR="0;34" ++CPE_NAME="cpe:/o:ibm:kvmibm:1.1.1" ++BUILD_ID="20160316" +diff --git a/third_party/python/distro/tests/resources/distros/kvmibm1/etc/redhat-release b/third_party/python/distro/tests/resources/distros/kvmibm1/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/kvmibm1/etc/redhat-release +@@ -0,0 +1,1 @@ ++KVM for IBM z Systems release 1.1.1 (Z) +diff --git a/third_party/python/distro/tests/resources/distros/kvmibm1/etc/system-release b/third_party/python/distro/tests/resources/distros/kvmibm1/etc/system-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/kvmibm1/etc/system-release +@@ -0,0 +1,1 @@ ++KVM for IBM z Systems release 1.1.1 (Z) +diff --git a/third_party/python/distro/tests/resources/distros/linuxmint17/bin/lsb_release b/third_party/python/distro/tests/resources/distros/linuxmint17/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/linuxmint17/bin/lsb_release +@@ -0,0 +1,43 @@ ++#!/bin/bash ++# ++# lsb_release command for testing the ld module. ++# Only the -a option is supported. ++# ++# This version of the lsb_release command reads an lsb-release file. ++# ++# The lsb-release file has the usual format, e.g.: ++# DISTRIB_ID=Ubuntu ++# DISTRIB_RELEASE=14.04 ++# DISTRIB_CODENAME=trusty ++# DISTRIB_DESCRIPTION="Ubuntu 14.04.3 LTS" ++# Where each line is optional. If a line is missing, the default value ++# will be the empty string. ++# ++ ++if [[ "$@" != "-a" ]]; then ++ echo "Usage: lsb_release -a" ++ exit 2 ++fi ++ ++# Because the PATH is set to just this directory, we cannot use 'dirname' ++# or other external programs, but need to use built-in abilities of bash. ++LSB_FILE="${0%/*}/../etc/lsb-release" ++ ++if [[ ! -f $LSB_FILE ]]; then ++ echo "Error: LSB release file does not exist: $LSB_FILE" ++ exit 1 ++fi ++ ++source $LSB_FILE ++ ++if [[ -n $LSB_VERSION ]]; then ++ echo "LSB Version: $LSB_VERSION" ++else ++ echo "No LSB modules are available." ++fi ++echo "Distributor ID: ${DISTRIB_ID:-}" ++echo "Description: ${DISTRIB_DESCRIPTION:-}" ++echo "Release: ${DISTRIB_RELEASE:-}" ++echo "Codename: ${DISTRIB_CODENAME:-}" ++ ++exit 0 +diff --git a/third_party/python/distro/tests/resources/distros/linuxmint17/etc/debian_version b/third_party/python/distro/tests/resources/distros/linuxmint17/etc/debian_version +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/linuxmint17/etc/debian_version +@@ -0,0 +1,1 @@ ++jessie/sid +diff --git a/third_party/python/distro/tests/resources/distros/linuxmint17/etc/lsb-release b/third_party/python/distro/tests/resources/distros/linuxmint17/etc/lsb-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/linuxmint17/etc/lsb-release +@@ -0,0 +1,4 @@ ++DISTRIB_ID=LinuxMint ++DISTRIB_RELEASE=17.3 ++DISTRIB_CODENAME=rosa ++DISTRIB_DESCRIPTION="Linux Mint 17.3 Rosa" +diff --git a/third_party/python/distro/tests/resources/distros/linuxmint17/etc/os-release b/third_party/python/distro/tests/resources/distros/linuxmint17/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/linuxmint17/etc/os-release +@@ -0,0 +1,9 @@ ++NAME="Ubuntu" ++VERSION="14.04.3 LTS, Trusty Tahr" ++ID=ubuntu ++ID_LIKE=debian ++PRETTY_NAME="Ubuntu 14.04.3 LTS" ++VERSION_ID="14.04" ++HOME_URL="http://www.ubuntu.com/" ++SUPPORT_URL="http://help.ubuntu.com/" ++BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" +diff --git a/third_party/python/distro/tests/resources/distros/linuxmint17/etc/upstream-release/lsb-release b/third_party/python/distro/tests/resources/distros/linuxmint17/etc/upstream-release/lsb-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/linuxmint17/etc/upstream-release/lsb-release +@@ -0,0 +1,4 @@ ++DISTRIB_ID=Ubuntu ++DISTRIB_RELEASE=14.04 ++DISTRIB_CODENAME=trusty ++DISTRIB_DESCRIPTION="Ubuntu 14.04 LTS" +diff --git a/third_party/python/distro/tests/resources/distros/mageia5/bin/lsb_release b/third_party/python/distro/tests/resources/distros/mageia5/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mageia5/bin/lsb_release +@@ -0,0 +1,39 @@ ++#!/bin/bash ++# ++# lsb_release command for testing the ld module. ++# Only the -a option is supported. ++# ++# This version of the lsb_release command reads an lsb-release file. ++# ++# The lsb-release file has the usual format, e.g.: ++# DISTRIB_ID=Ubuntu ++# DISTRIB_RELEASE=14.04 ++# DISTRIB_CODENAME=trusty ++# DISTRIB_DESCRIPTION="Ubuntu 14.04.3 LTS" ++# Where each line is optional. If a line is missing, the default value ++# will be the empty string. ++# ++ ++if [[ "$@" != "-a" ]]; then ++ echo "Usage: lsb_release -a" ++ exit 2 ++fi ++ ++# Because the PATH is set to just this directory, we cannot use 'dirname' ++# or other external programs, but need to use built-in abilities of bash. ++LSB_FILE="${0%/*}/../etc/lsb-release" ++ ++if [[ ! -f $LSB_FILE ]]; then ++ echo "Error: LSB release file does not exist: $LSB_FILE" ++ exit 1 ++fi ++ ++source $LSB_FILE ++ ++echo "LSB Version: ${LSB_VERSION:-*}" ++echo "Distributor ID: ${DISTRIB_ID:-}" ++echo "Description: ${DISTRIB_DESCRIPTION:-}" ++echo "Release: ${DISTRIB_RELEASE:-}" ++echo "Codename: ${DISTRIB_CODENAME:-}" ++ ++exit 0 +diff --git a/third_party/python/distro/tests/resources/distros/mageia5/etc/lsb-release b/third_party/python/distro/tests/resources/distros/mageia5/etc/lsb-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mageia5/etc/lsb-release +@@ -0,0 +1,5 @@ ++LSB_VERSION= ++DISTRIB_ID="Mageia" ++DISTRIB_RELEASE=5 ++DISTRIB_CODENAME=thornicroft ++DISTRIB_DESCRIPTION="Mageia 5" +diff --git a/third_party/python/distro/tests/resources/distros/mageia5/etc/mageia-release b/third_party/python/distro/tests/resources/distros/mageia5/etc/mageia-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mageia5/etc/mageia-release +@@ -0,0 +1,1 @@ ++Mageia release 5 (Official) for x86_64 +diff --git a/third_party/python/distro/tests/resources/distros/mageia5/etc/mandrake-release b/third_party/python/distro/tests/resources/distros/mageia5/etc/mandrake-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mageia5/etc/mandrake-release +@@ -0,0 +1,1 @@ ++Mageia release 5 (Official) for x86_64 +diff --git a/third_party/python/distro/tests/resources/distros/mageia5/etc/mandrakelinux-release b/third_party/python/distro/tests/resources/distros/mageia5/etc/mandrakelinux-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mageia5/etc/mandrakelinux-release +@@ -0,0 +1,1 @@ ++Mageia release 5 (Official) for x86_64 +diff --git a/third_party/python/distro/tests/resources/distros/mageia5/etc/mandriva-release b/third_party/python/distro/tests/resources/distros/mageia5/etc/mandriva-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mageia5/etc/mandriva-release +@@ -0,0 +1,1 @@ ++Mageia release 5 (Official) for x86_64 +diff --git a/third_party/python/distro/tests/resources/distros/mageia5/etc/os-release b/third_party/python/distro/tests/resources/distros/mageia5/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mageia5/etc/os-release +@@ -0,0 +1,10 @@ ++NAME="Mageia" ++VERSION="5" ++ID=mageia ++VERSION_ID=5 ++ID_LIKE="mandriva fedora" ++PRETTY_NAME="Mageia 5" ++ANSI_COLOR="1;36" ++HOME_URL="http://www.mageia.org/" ++SUPPORT_URL="http://www.mageia.org/support/" ++BUG_REPORT_URL="https://bugs.mageia.org/" +diff --git a/third_party/python/distro/tests/resources/distros/mageia5/etc/redhat-release b/third_party/python/distro/tests/resources/distros/mageia5/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mageia5/etc/redhat-release +@@ -0,0 +1,1 @@ ++Mageia release 5 (Official) for x86_64 +diff --git a/third_party/python/distro/tests/resources/distros/mageia5/etc/release b/third_party/python/distro/tests/resources/distros/mageia5/etc/release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mageia5/etc/release +@@ -0,0 +1,1 @@ ++Mageia release 5 (Official) for x86_64 +diff --git a/third_party/python/distro/tests/resources/distros/mageia5/etc/version b/third_party/python/distro/tests/resources/distros/mageia5/etc/version +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mageia5/etc/version +@@ -0,0 +1,1 @@ ++5 2 official +diff --git a/third_party/python/distro/tests/resources/distros/mageia5/usr/lib/os-release b/third_party/python/distro/tests/resources/distros/mageia5/usr/lib/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mageia5/usr/lib/os-release +@@ -0,0 +1,10 @@ ++NAME="Mageia" ++VERSION="5" ++ID=mageia ++VERSION_ID=5 ++ID_LIKE="mandriva fedora" ++PRETTY_NAME="Mageia 5" ++ANSI_COLOR="1;36" ++HOME_URL="http://www.mageia.org/" ++SUPPORT_URL="http://www.mageia.org/support/" ++BUG_REPORT_URL="https://bugs.mageia.org/" +diff --git a/third_party/python/distro/tests/resources/distros/mandriva2011/bin/lsb_release b/third_party/python/distro/tests/resources/distros/mandriva2011/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mandriva2011/bin/lsb_release +@@ -0,0 +1,39 @@ ++#!/bin/bash ++# ++# lsb_release command for testing the ld module. ++# Only the -a option is supported. ++# ++# This version of the lsb_release command reads an lsb-release file. ++# ++# The lsb-release file has the usual format, e.g.: ++# DISTRIB_ID=Ubuntu ++# DISTRIB_RELEASE=14.04 ++# DISTRIB_CODENAME=trusty ++# DISTRIB_DESCRIPTION="Ubuntu 14.04.3 LTS" ++# Where each line is optional. If a line is missing, the default value ++# will be the empty string. ++# ++ ++if [[ "$@" != "-a" ]]; then ++ echo "Usage: lsb_release -a" ++ exit 2 ++fi ++ ++# Because the PATH is set to just this directory, we cannot use 'dirname' ++# or other external programs, but need to use built-in abilities of bash. ++LSB_FILE="${0%/*}/../etc/lsb-release" ++ ++if [[ ! -f $LSB_FILE ]]; then ++ echo "Error: LSB release file does not exist: $LSB_FILE" ++ exit 1 ++fi ++ ++source $LSB_FILE ++ ++echo "LSB Version: ${LSB_VERSION:-*}" ++echo "Distributor ID: ${DISTRIB_ID:-}" ++echo "Description: ${DISTRIB_DESCRIPTION:-}" ++echo "Release: ${DISTRIB_RELEASE:-}" ++echo "Codename: ${DISTRIB_CODENAME:-}" ++ ++exit 0 +diff --git a/third_party/python/distro/tests/resources/distros/mandriva2011/etc/lsb-release b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/lsb-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/lsb-release +@@ -0,0 +1,5 @@ ++LSB_VERSION= ++DISTRIB_ID=MandrivaLinux ++DISTRIB_RELEASE=2011.0 ++DISTRIB_CODENAME=turtle ++DISTRIB_DESCRIPTION="Mandriva Linux 2011.0" +diff --git a/third_party/python/distro/tests/resources/distros/mandriva2011/etc/mandrake-release b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/mandrake-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/mandrake-release +@@ -0,0 +1,1 @@ ++Mandriva Linux release 2011.0 (Official) for x86_64 +diff --git a/third_party/python/distro/tests/resources/distros/mandriva2011/etc/mandrakelinux-release b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/mandrakelinux-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/mandrakelinux-release +@@ -0,0 +1,1 @@ ++Mandriva Linux release 2011.0 (Official) for x86_64 +diff --git a/third_party/python/distro/tests/resources/distros/mandriva2011/etc/mandriva-release b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/mandriva-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/mandriva-release +@@ -0,0 +1,1 @@ ++Mandriva Linux release 2011.0 (Official) for x86_64 +diff --git a/third_party/python/distro/tests/resources/distros/mandriva2011/etc/redhat-release b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/redhat-release +@@ -0,0 +1,1 @@ ++Mandriva Linux release 2011.0 (Official) for x86_64 +diff --git a/third_party/python/distro/tests/resources/distros/mandriva2011/etc/release b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/release +@@ -0,0 +1,1 @@ ++Mandriva Linux release 2011.0 (Official) for x86_64 +diff --git a/third_party/python/distro/tests/resources/distros/mandriva2011/etc/version b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/version +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/mandriva2011/etc/version +@@ -0,0 +1,1 @@ ++2011.0.0 2 cooker +diff --git a/third_party/python/distro/tests/resources/distros/manjaro1512/bin/lsb_release b/third_party/python/distro/tests/resources/distros/manjaro1512/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/manjaro1512/bin/lsb_release +@@ -0,0 +1,43 @@ ++#!/bin/bash ++# ++# lsb_release command for testing the ld module. ++# Only the -a option is supported. ++# ++# This version of the lsb_release command reads an lsb-release file. ++# ++# The lsb-release file has the usual format, e.g.: ++# DISTRIB_ID=Ubuntu ++# DISTRIB_RELEASE=14.04 ++# DISTRIB_CODENAME=trusty ++# DISTRIB_DESCRIPTION="Ubuntu 14.04.3 LTS" ++# Where each line is optional. If a line is missing, the default value ++# will be the empty string. ++# ++ ++if [[ "$@" != "-a" ]]; then ++ echo "Usage: lsb_release -a" ++ exit 2 ++fi ++ ++# Because the PATH is set to just this directory, we cannot use 'dirname' ++# or other external programs, but need to use built-in abilities of bash. ++LSB_FILE="${0%/*}/../etc/lsb-release" ++ ++if [[ ! -f $LSB_FILE ]]; then ++ echo "Error: LSB release file does not exist: $LSB_FILE" ++ exit 1 ++fi ++ ++source $LSB_FILE ++ ++if [[ -n $LSB_VERSION ]]; then ++ echo "LSB Version: $LSB_VERSION" ++else ++ echo "No LSB modules are available." ++fi ++echo "Distributor ID: ${DISTRIB_ID:-}" ++echo "Description: ${DISTRIB_DESCRIPTION:-}" ++echo "Release: ${DISTRIB_RELEASE:-}" ++echo "Codename: ${DISTRIB_CODENAME:-}" ++ ++exit 0 +diff --git a/third_party/python/distro/tests/resources/distros/manjaro1512/etc/lsb-release b/third_party/python/distro/tests/resources/distros/manjaro1512/etc/lsb-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/manjaro1512/etc/lsb-release +@@ -0,0 +1,4 @@ ++DISTRIB_ID=ManjaroLinux ++DISTRIB_RELEASE=15.12 ++DISTRIB_CODENAME=Capella ++DISTRIB_DESCRIPTION="Manjaro Linux" +diff --git a/third_party/python/distro/tests/resources/distros/manjaro1512/etc/manjaro-release b/third_party/python/distro/tests/resources/distros/manjaro1512/etc/manjaro-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/manjaro1512/etc/manjaro-release +@@ -0,0 +1,1 @@ ++Manjaro Linux +diff --git a/third_party/python/distro/tests/resources/distros/manjaro1512/etc/os-release b/third_party/python/distro/tests/resources/distros/manjaro1512/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/manjaro1512/etc/os-release +@@ -0,0 +1,7 @@ ++NAME="Manjaro Linux" ++ID=manjaro ++PRETTY_NAME="Manjaro Linux" ++ANSI_COLOR="1;32" ++HOME_URL="http://www.manjaro.org/" ++SUPPORT_URL="http://www.manjaro.org/" ++BUG_REPORT_URL="http://bugs.manjaro.org/" +diff --git a/third_party/python/distro/tests/resources/distros/netbsd711/bin/uname b/third_party/python/distro/tests/resources/distros/netbsd711/bin/uname +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/netbsd711/bin/uname +@@ -0,0 +1,4 @@ ++#!/bin/sh ++ ++echo "NetBSD 7.1.1" ++ +diff --git a/third_party/python/distro/tests/resources/distros/openbsd62/bin/uname b/third_party/python/distro/tests/resources/distros/openbsd62/bin/uname +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/openbsd62/bin/uname +@@ -0,0 +1,4 @@ ++#!/bin/sh ++ ++echo "OpenBSD 6.2" ++ +diff --git a/third_party/python/distro/tests/resources/distros/openelec6/etc/os-release b/third_party/python/distro/tests/resources/distros/openelec6/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/openelec6/etc/os-release +@@ -0,0 +1,9 @@ ++NAME="OpenELEC" ++VERSION="6.0.3" ++ID="openelec" ++VERSION_ID="6.0" ++PRETTY_NAME="OpenELEC (official) - Version: 6.0.3" ++HOME_URL="http://www.openelec.tv" ++BUG_REPORT_URL="https://github.com/OpenELEC/OpenELEC.tv" ++OPENELEC_ARCH="imx6.arm" ++OPENELEC_BUILD="official" +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/opensuse42/etc/SuSE-release b/third_party/python/distro/tests/resources/distros/opensuse42/etc/SuSE-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/opensuse42/etc/SuSE-release +@@ -0,0 +1,1 @@ ++openSUSE 42.1 (x86_64) +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/opensuse42/etc/os-release b/third_party/python/distro/tests/resources/distros/opensuse42/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/opensuse42/etc/os-release +@@ -0,0 +1,10 @@ ++NAME="openSUSE Leap" ++VERSION="42.1" ++VERSION_ID="42.1" ++PRETTY_NAME="openSUSE Leap 42.1 (x86_64)" ++ID=opensuse ++ANSI_COLOR="0;32" ++CPE_NAME="cpe:/o:opensuse:opensuse:42.1" ++BUG_REPORT_URL="https://bugs.opensuse.org" ++HOME_URL="https://opensuse.org/" ++ID_LIKE="suse" +diff --git a/third_party/python/distro/tests/resources/distros/oracle7/etc/oracle-release b/third_party/python/distro/tests/resources/distros/oracle7/etc/oracle-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/oracle7/etc/oracle-release +@@ -0,0 +1,1 @@ ++Oracle Linux Server release 7.5 +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/oracle7/etc/os-release b/third_party/python/distro/tests/resources/distros/oracle7/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/oracle7/etc/os-release +@@ -0,0 +1,14 @@ ++NAME="Oracle Linux Server" ++VERSION="7.5" ++ID="ol" ++VERSION_ID="7.5" ++PRETTY_NAME="Oracle Linux Server 7.5" ++ANSI_COLOR="0;31" ++CPE_NAME="cpe:/o:oracle:linux:7:5:server" ++HOME_URL="https://linux.oracle.com/" ++BUG_REPORT_URL="https://bugzilla.oracle.com/" ++ ++ORACLE_BUGZILLA_PRODUCT="Oracle Linux 7" ++ORACLE_BUGZILLA_PRODUCT_VERSION=7.5 ++ORACLE_SUPPORT_PRODUCT="Oracle Linux" ++ORACLE_SUPPORT_PRODUCT_VERSION=7.5 +diff --git a/third_party/python/distro/tests/resources/distros/raspbian7/etc/debian_version b/third_party/python/distro/tests/resources/distros/raspbian7/etc/debian_version +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/raspbian7/etc/debian_version +@@ -0,0 +1,1 @@ ++7.1 +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/raspbian7/etc/os-release b/third_party/python/distro/tests/resources/distros/raspbian7/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/raspbian7/etc/os-release +@@ -0,0 +1,10 @@ ++PRETTY_NAME="Raspbian GNU/Linux 7 (wheezy)" ++NAME="Raspbian GNU/Linux" ++VERSION_ID="7" ++VERSION="7 (wheezy)" ++ID=raspbian ++ID_LIKE=debian ++ANSI_COLOR="1;31" ++HOME_URL="http://www.raspbian.org/" ++SUPPORT_URL="http://www.raspbian.org/RaspbianForums" ++BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs" +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/raspbian7/etc/os-release.orig b/third_party/python/distro/tests/resources/distros/raspbian7/etc/os-release.orig +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/raspbian7/etc/os-release.orig +@@ -0,0 +1,9 @@ ++PRETTY_NAME="Debian #OSNAME# 7 (wheezy)" ++NAME="Debian #OSNAME#" ++VERSION_ID="7" ++VERSION="7 (wheezy)" ++ID=debian ++ANSI_COLOR="1;31" ++HOME_URL="http://www.debian.org/" ++SUPPORT_URL="http://www.debian.org/support/" ++BUG_REPORT_URL="http://bugs.debian.org/" +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/raspbian8/etc/debian_version b/third_party/python/distro/tests/resources/distros/raspbian8/etc/debian_version +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/raspbian8/etc/debian_version +@@ -0,0 +1,1 @@ ++8.0 +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/raspbian8/etc/os-release b/third_party/python/distro/tests/resources/distros/raspbian8/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/raspbian8/etc/os-release +@@ -0,0 +1,9 @@ ++PRETTY_NAME="Raspbian GNU/Linux 8 (jessie)" ++NAME="Raspbian GNU/Linux" ++VERSION_ID="8" ++VERSION="8 (jessie)" ++ID=raspbian ++ID_LIKE=debian ++HOME_URL="http://www.raspbian.org/" ++SUPPORT_URL="http://www.raspbian.org/RaspbianForums" ++BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs" +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/rhel5/etc/redhat-release b/third_party/python/distro/tests/resources/distros/rhel5/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/rhel5/etc/redhat-release +@@ -0,0 +1,1 @@ ++Red Hat Enterprise Linux Server release 5.11 (Tikanga) +diff --git a/third_party/python/distro/tests/resources/distros/rhel6/etc/redhat-release b/third_party/python/distro/tests/resources/distros/rhel6/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/rhel6/etc/redhat-release +@@ -0,0 +1,1 @@ ++Red Hat Enterprise Linux Server release 6.5 (Santiago) +diff --git a/third_party/python/distro/tests/resources/distros/rhel6/etc/system-release b/third_party/python/distro/tests/resources/distros/rhel6/etc/system-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/rhel6/etc/system-release +@@ -0,0 +1,1 @@ ++Red Hat Enterprise Linux Server release 6.5 (Santiago) +diff --git a/third_party/python/distro/tests/resources/distros/rhel7/etc/os-release b/third_party/python/distro/tests/resources/distros/rhel7/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/rhel7/etc/os-release +@@ -0,0 +1,15 @@ ++NAME="Red Hat Enterprise Linux Server" ++VERSION="7.0 (Maipo)" ++ID="rhel" ++ID_LIKE="fedora" ++VERSION_ID="7.0" ++PRETTY_NAME="Red Hat Enterprise Linux Server 7.0 (Maipo)" ++ANSI_COLOR="0;31" ++CPE_NAME="cpe:/o:redhat:enterprise_linux:7.0:GA:server" ++HOME_URL="https://www.redhat.com/" ++BUG_REPORT_URL="https://bugzilla.redhat.com/" ++ ++REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 7" ++REDHAT_BUGZILLA_PRODUCT_VERSION=7.0 ++REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux" ++REDHAT_SUPPORT_PRODUCT_VERSION=7.0 +diff --git a/third_party/python/distro/tests/resources/distros/rhel7/etc/redhat-release b/third_party/python/distro/tests/resources/distros/rhel7/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/rhel7/etc/redhat-release +@@ -0,0 +1,1 @@ ++Red Hat Enterprise Linux Server release 7.0 (Maipo) +diff --git a/third_party/python/distro/tests/resources/distros/rhel7/etc/system-release b/third_party/python/distro/tests/resources/distros/rhel7/etc/system-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/rhel7/etc/system-release +@@ -0,0 +1,1 @@ ++Red Hat Enterprise Linux Server release 7.0 (Maipo) +diff --git a/third_party/python/distro/tests/resources/distros/scientific6/etc/redhat-release b/third_party/python/distro/tests/resources/distros/scientific6/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/scientific6/etc/redhat-release +@@ -0,0 +1,1 @@ ++Scientific Linux release 6.4 (Carbon) +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/scientific6/etc/system-release b/third_party/python/distro/tests/resources/distros/scientific6/etc/system-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/scientific6/etc/system-release +@@ -0,0 +1,1 @@ ++Scientific Linux release 6.4 (Carbon) +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/scientific7/etc/os-release b/third_party/python/distro/tests/resources/distros/scientific7/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/scientific7/etc/os-release +@@ -0,0 +1,15 @@ ++NAME="Scientific Linux" ++VERSION="7.2 (Nitrogen)" ++ID="rhel" ++ID_LIKE="fedora" ++VERSION_ID="7.2" ++PRETTY_NAME="Scientific Linux 7.2 (Nitrogen)" ++ANSI_COLOR="0;31" ++CPE_NAME="cpe:/o:scientificlinux:scientificlinux:7.2:GA" ++HOME_URL="http://www.scientificlinux.org//" ++BUG_REPORT_URL="mailto:scientific-linux-devel@listserv.fnal.gov" ++ ++REDHAT_BUGZILLA_PRODUCT="Scientific Linux 7" ++REDHAT_BUGZILLA_PRODUCT_VERSION=7.2 ++REDHAT_SUPPORT_PRODUCT="Scientific Linux" ++REDHAT_SUPPORT_PRODUCT_VERSION="7.2" +diff --git a/third_party/python/distro/tests/resources/distros/scientific7/etc/redhat-release b/third_party/python/distro/tests/resources/distros/scientific7/etc/redhat-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/scientific7/etc/redhat-release +@@ -0,0 +1,1 @@ ++Scientific Linux release 7.2 (Nitrogen) +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/scientific7/etc/sl-release b/third_party/python/distro/tests/resources/distros/scientific7/etc/sl-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/scientific7/etc/sl-release +@@ -0,0 +1,1 @@ ++Scientific Linux release 7.2 (Nitrogen) +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/scientific7/etc/system-release b/third_party/python/distro/tests/resources/distros/scientific7/etc/system-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/scientific7/etc/system-release +@@ -0,0 +1,1 @@ ++Scientific Linux release 7.2 (Nitrogen) +\ No newline at end of file +diff --git a/third_party/python/distro/tests/resources/distros/slackware14/etc/os-release b/third_party/python/distro/tests/resources/distros/slackware14/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/slackware14/etc/os-release +@@ -0,0 +1,10 @@ ++NAME=Slackware ++VERSION="14.1" ++ID=slackware ++VERSION_ID=14.1 ++PRETTY_NAME="Slackware 14.1" ++ANSI_COLOR="0;34" ++CPE_NAME="cpe:/o:slackware:slackware_linux:14.1" ++HOME_URL="http://slackware.com/" ++SUPPORT_URL="http://www.linuxquestions.org/questions/slackware-14/" ++BUG_REPORT_URL="http://www.linuxquestions.org/questions/slackware-14/" +diff --git a/third_party/python/distro/tests/resources/distros/slackware14/etc/slackware-version b/third_party/python/distro/tests/resources/distros/slackware14/etc/slackware-version +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/slackware14/etc/slackware-version +@@ -0,0 +1,1 @@ ++Slackware 14.1 +diff --git a/third_party/python/distro/tests/resources/distros/sles12/bin/lsb_release b/third_party/python/distro/tests/resources/distros/sles12/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/sles12/bin/lsb_release +@@ -0,0 +1,21 @@ ++#!/bin/bash ++# ++# lsb_release command for testing the ld module. ++# Only the -a option is supported. ++# ++# This version of the lsb_release command works without a corresponding ++# etc/lsb-release file. ++# ++ ++if [[ "$@" != "-a" ]]; then ++ echo "Usage: lsb_release -a" ++ exit 2 ++fi ++ ++echo "LSB Version: n/a" ++echo "Distributor ID: SUSE LINUX" ++echo "Description: SUSE Linux Enterprise Server 12 SP1" ++echo "Release: 12.1" ++echo "Codename: n/a" ++ ++exit 0 +diff --git a/third_party/python/distro/tests/resources/distros/sles12/etc/SuSE-release b/third_party/python/distro/tests/resources/distros/sles12/etc/SuSE-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/sles12/etc/SuSE-release +@@ -0,0 +1,5 @@ ++SUSE Linux Enterprise Server 12 (s390x) ++VERSION = 12 ++PATCHLEVEL = 1 ++# This file is deprecated and will be removed in a future service pack or release. ++# Please check /etc/os-release for details about this release. +diff --git a/third_party/python/distro/tests/resources/distros/sles12/etc/os-release b/third_party/python/distro/tests/resources/distros/sles12/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/sles12/etc/os-release +@@ -0,0 +1,7 @@ ++NAME="SLES" ++VERSION="12-SP1" ++VERSION_ID="12.1" ++PRETTY_NAME="SUSE Linux Enterprise Server 12 SP1" ++ID="sles" ++ANSI_COLOR="0;32" ++CPE_NAME="cpe:/o:suse:sles:12:sp1" +diff --git a/third_party/python/distro/tests/resources/distros/ubuntu14/bin/lsb_release b/third_party/python/distro/tests/resources/distros/ubuntu14/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/ubuntu14/bin/lsb_release +@@ -0,0 +1,39 @@ ++#!/bin/bash ++# ++# lsb_release command for testing the ld module. ++# Only the -a option is supported. ++# ++# This version of the lsb_release command reads an lsb-release file. ++# ++# The lsb-release file has the usual format, e.g.: ++# DISTRIB_ID=Ubuntu ++# DISTRIB_RELEASE=14.04 ++# DISTRIB_CODENAME=trusty ++# DISTRIB_DESCRIPTION="Ubuntu 14.04.3 LTS" ++# Where each line is optional. If a line is missing, the default value ++# will be the empty string. ++# ++ ++if [[ "$@" != "-a" ]]; then ++ echo "Usage: lsb_release -a" ++ exit 2 ++fi ++ ++# Because the PATH is set to just this directory, we cannot use 'dirname' ++# or other external programs, but need to use built-in abilities of bash. ++LSB_FILE="${0%/*}/../etc/lsb-release" ++ ++if [[ ! -f $LSB_FILE ]]; then ++ echo "Error: LSB release file does not exist: $LSB_FILE" ++ exit 1 ++fi ++ ++source $LSB_FILE ++ ++echo "No LSB modules are available." ++echo "Distributor ID: ${DISTRIB_ID:-}" ++echo "Description: ${DISTRIB_DESCRIPTION:-}" ++echo "Release: ${DISTRIB_RELEASE:-}" ++echo "Codename: ${DISTRIB_CODENAME:-}" ++ ++exit 0 +diff --git a/third_party/python/distro/tests/resources/distros/ubuntu14/etc/debian_version b/third_party/python/distro/tests/resources/distros/ubuntu14/etc/debian_version +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/ubuntu14/etc/debian_version +@@ -0,0 +1,1 @@ ++jessie/sid +diff --git a/third_party/python/distro/tests/resources/distros/ubuntu14/etc/lsb-release b/third_party/python/distro/tests/resources/distros/ubuntu14/etc/lsb-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/ubuntu14/etc/lsb-release +@@ -0,0 +1,4 @@ ++DISTRIB_ID=Ubuntu ++DISTRIB_RELEASE=14.04 ++DISTRIB_CODENAME=trusty ++DISTRIB_DESCRIPTION="Ubuntu 14.04.3 LTS" +diff --git a/third_party/python/distro/tests/resources/distros/ubuntu14/etc/os-release b/third_party/python/distro/tests/resources/distros/ubuntu14/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/ubuntu14/etc/os-release +@@ -0,0 +1,9 @@ ++NAME="Ubuntu" ++VERSION="14.04.3 LTS, Trusty Tahr" ++ID=ubuntu ++ID_LIKE=debian ++PRETTY_NAME="Ubuntu 14.04.3 LTS" ++VERSION_ID="14.04" ++HOME_URL="http://www.ubuntu.com/" ++SUPPORT_URL="http://help.ubuntu.com/" ++BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" +diff --git a/third_party/python/distro/tests/resources/distros/ubuntu16/bin/lsb_release b/third_party/python/distro/tests/resources/distros/ubuntu16/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/ubuntu16/bin/lsb_release +@@ -0,0 +1,39 @@ ++#!/bin/bash ++# ++# lsb_release command for testing the ld module. ++# Only the -a option is supported. ++# ++# This version of the lsb_release command reads an lsb-release file. ++# ++# The lsb-release file has the usual format, e.g.: ++# DISTRIB_ID=Ubuntu ++# DISTRIB_RELEASE=14.04 ++# DISTRIB_CODENAME=trusty ++# DISTRIB_DESCRIPTION="Ubuntu 14.04.3 LTS" ++# Where each line is optional. If a line is missing, the default value ++# will be the empty string. ++# ++ ++if [[ "$@" != "-a" ]]; then ++ echo "Usage: lsb_release -a" ++ exit 2 ++fi ++ ++# Because the PATH is set to just this directory, we cannot use 'dirname' ++# or other external programs, but need to use built-in abilities of bash. ++LSB_FILE="${0%/*}/../etc/lsb-release" ++ ++if [[ ! -f $LSB_FILE ]]; then ++ echo "Error: LSB release file does not exist: $LSB_FILE" ++ exit 1 ++fi ++ ++source $LSB_FILE ++ ++echo "No LSB modules are available." ++echo "Distributor ID: ${DISTRIB_ID:-}" ++echo "Description: ${DISTRIB_DESCRIPTION:-}" ++echo "Release: ${DISTRIB_RELEASE:-}" ++echo "Codename: ${DISTRIB_CODENAME:-}" ++ ++exit 0 +diff --git a/third_party/python/distro/tests/resources/distros/ubuntu16/etc/debian_version b/third_party/python/distro/tests/resources/distros/ubuntu16/etc/debian_version +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/ubuntu16/etc/debian_version +@@ -0,0 +1,1 @@ ++stretch/sid +diff --git a/third_party/python/distro/tests/resources/distros/ubuntu16/etc/lsb-release b/third_party/python/distro/tests/resources/distros/ubuntu16/etc/lsb-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/ubuntu16/etc/lsb-release +@@ -0,0 +1,4 @@ ++DISTRIB_ID=Ubuntu ++DISTRIB_RELEASE=16.04 ++DISTRIB_CODENAME=xenial ++DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS" +diff --git a/third_party/python/distro/tests/resources/distros/ubuntu16/etc/os-release b/third_party/python/distro/tests/resources/distros/ubuntu16/etc/os-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/distros/ubuntu16/etc/os-release +@@ -0,0 +1,10 @@ ++NAME="Ubuntu" ++VERSION="16.04.1 LTS (Xenial Xerus)" ++ID=ubuntu ++ID_LIKE=debian ++PRETTY_NAME="Ubuntu 16.04.1 LTS" ++VERSION_ID="16.04" ++HOME_URL="http://www.ubuntu.com/" ++SUPPORT_URL="http://help.ubuntu.com/" ++BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" ++UBUNTU_CODENAME=xenial +diff --git a/third_party/python/distro/tests/resources/special/empty-release b/third_party/python/distro/tests/resources/special/empty-release +new file mode 100644 +diff --git a/third_party/python/distro/tests/resources/testdistros/distro/baduname/bin/uname b/third_party/python/distro/tests/resources/testdistros/distro/baduname/bin/uname +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/testdistros/distro/baduname/bin/uname +@@ -0,0 +1,2 @@ ++#!/bin/sh ++echo "I'm a bad uname file!" +diff --git a/third_party/python/distro/tests/resources/testdistros/distro/unknowndistro/etc/unknowndistro-release b/third_party/python/distro/tests/resources/testdistros/distro/unknowndistro/etc/unknowndistro-release +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/testdistros/distro/unknowndistro/etc/unknowndistro-release +@@ -0,0 +1,1 @@ ++Unknown Distro release 1.0 (Unknown Codename) +diff --git a/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc001/bin/lsb_release b/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc001/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc001/bin/lsb_release +@@ -0,0 +1,5 @@ ++#!/bin/bash ++rc=1 ++msg="General error" ++echo "Test failure - exiting with $rc ($msg)" ++exit $rc +diff --git a/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc002/bin/lsb_release b/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc002/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc002/bin/lsb_release +@@ -0,0 +1,5 @@ ++#!/bin/bash ++rc=2 ++msg="Misuse of shell builtins, or missing keyword or command" ++echo "Test failure - exiting with $rc ($msg)" ++exit $rc +diff --git a/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc126/bin/lsb_release b/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc126/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc126/bin/lsb_release +@@ -0,0 +1,5 @@ ++#!/bin/bash ++rc=126 ++msg="Cannot execute command" ++echo "Test failure - exiting with $rc ($msg)" ++exit $rc +diff --git a/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc130/bin/lsb_release b/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc130/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc130/bin/lsb_release +@@ -0,0 +1,5 @@ ++#!/bin/bash ++rc=130 ++msg="Signal 2 - Script terminated with Ctrl-C" ++echo "Test failure - exiting with $rc ($msg)" ++exit $rc +diff --git a/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc255/bin/lsb_release b/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc255/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/testdistros/lsb/lsb_rc255/bin/lsb_release +@@ -0,0 +1,5 @@ ++#!/bin/bash ++rc=255 ++msg="Exit code out of range" ++echo "Test failure - exiting with $rc ($msg)" ++exit $rc +diff --git a/third_party/python/distro/tests/resources/testdistros/lsb/ubuntu14_nomodules/bin/lsb_release b/third_party/python/distro/tests/resources/testdistros/lsb/ubuntu14_nomodules/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/testdistros/lsb/ubuntu14_nomodules/bin/lsb_release +@@ -0,0 +1,8 @@ ++#!/bin/bash ++/bin/cat <<'EOT' ++No LSB modules are available. ++Distributor ID: Ubuntu ++Description: Ubuntu 14.04.3 LTS ++Release: 14.04 ++Codename: trusty ++EOT +diff --git a/third_party/python/distro/tests/resources/testdistros/lsb/ubuntu14_normal/bin/lsb_release b/third_party/python/distro/tests/resources/testdistros/lsb/ubuntu14_normal/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/testdistros/lsb/ubuntu14_normal/bin/lsb_release +@@ -0,0 +1,7 @@ ++#!/bin/bash ++/bin/cat <<'EOT' ++Distributor ID: Ubuntu ++Description: Ubuntu 14.04.3 LTS ++Release: 14.04 ++Codename: trusty ++EOT +diff --git a/third_party/python/distro/tests/resources/testdistros/lsb/ubuntu14_trailingblanks/bin/lsb_release b/third_party/python/distro/tests/resources/testdistros/lsb/ubuntu14_trailingblanks/bin/lsb_release +new file mode 100755 +--- /dev/null ++++ b/third_party/python/distro/tests/resources/testdistros/lsb/ubuntu14_trailingblanks/bin/lsb_release +@@ -0,0 +1,8 @@ ++#!/bin/bash ++/bin/cat <<'EOT' ++No LSB modules are available. ++Distributor ID: Ubuntu ++Description: Ubuntu 14.04.3 LTS ++Release: 14.04 ++Codename: trusty ++EOT +diff --git a/third_party/python/distro/tests/test_distro.py b/third_party/python/distro/tests/test_distro.py +new file mode 100644 +--- /dev/null ++++ b/third_party/python/distro/tests/test_distro.py +@@ -0,0 +1,2062 @@ ++# Copyright 2015,2016 Nir Cohen ++# ++# Licensed under the Apache License, Version 2.0 (the "License"); ++# you may not use this file except in compliance with the License. ++# You may obtain a copy of the License at ++# ++# http://www.apache.org/licenses/LICENSE-2.0 ++# ++# Unless required by applicable law or agreed to in writing, software ++# distributed under the License is distributed on an "AS IS" BASIS, ++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++# See the License for the specific language governing permissions and ++# limitations under the License. ++ ++import os ++import sys ++import ast ++import subprocess ++try: ++ from StringIO import StringIO # Python 2.x ++except ImportError: ++ from io import StringIO # Python 3.x ++ ++import pytest ++ ++ ++BASE = os.path.abspath(os.path.dirname(__file__)) ++RESOURCES = os.path.join(BASE, 'resources') ++DISTROS_DIR = os.path.join(RESOURCES, 'distros') ++TESTDISTROS = os.path.join(RESOURCES, 'testdistros') ++SPECIAL = os.path.join(RESOURCES, 'special') ++DISTROS = [dist for dist in os.listdir(DISTROS_DIR) if dist != '__shared__'] ++ ++ ++IS_LINUX = sys.platform.startswith('linux') ++if IS_LINUX: ++ import distro ++ ++ RELATIVE_UNIXCONFDIR = distro._UNIXCONFDIR[1:] ++ MODULE_DISTRO = distro._distro ++ ++ ++class TestNonLinuxPlatform: ++ """Obviously, this only tests Windows. Will add OS X tests on Travis ++ Later ++ """ ++ ++ def test_cant_use_on_windows(self): ++ try: ++ import distro # NOQA ++ except ImportError as ex: ++ assert 'Unsupported platform' in str(ex) ++ ++ ++@pytest.mark.skipif(not IS_LINUX, reason='Irrelevant on non-linux') ++class TestCli: ++ ++ def _parse(self, command): ++ sys.argv = command.split() ++ distro.main() ++ ++ def _run(self, command): ++ stdout, _ = subprocess.Popen( ++ command, ++ stdout=subprocess.PIPE, ++ stderr=subprocess.PIPE).communicate() ++ # Need to decode or we get bytes in Python 3.x ++ return stdout.decode('utf-8') ++ ++ def test_cli_for_coverage_yuch(self): ++ self._parse('distro') ++ self._parse('distro -j') ++ ++ def test_cli(self): ++ command = [sys.executable, '-m', 'distro'] ++ desired_output = 'Name: ' + distro.name(pretty=True) ++ distro_version = distro.version(pretty=True) ++ distro_codename = distro.codename() ++ desired_output += '\n' + 'Version: ' + distro_version ++ desired_output += '\n' + 'Codename: ' + distro_codename ++ desired_output += '\n' ++ assert self._run(command) == desired_output ++ ++ def test_cli_json(self): ++ command = [sys.executable, '-m', 'distro', '-j'] ++ assert ast.literal_eval(self._run(command)) == distro.info() ++ ++ ++@pytest.mark.skipif(not IS_LINUX, reason='Irrelevant on non-linux') ++class DistroTestCase(object): ++ """A base class for any testcase classes that test the distributions ++ represented in the `DISTROS` subtree. ++ """ ++ ++ def setup_method(self, test_method): ++ # The environment stays the same across all testcases, so we ++ # save and restore the PATH env var in each test case that ++ # changes it: ++ self._saved_path = os.environ["PATH"] ++ self._saved_UNIXCONFDIR = distro._UNIXCONFDIR ++ ++ def teardown_method(self, test_method): ++ os.environ["PATH"] = self._saved_path ++ distro._UNIXCONFDIR = self._saved_UNIXCONFDIR ++ ++ def _setup_for_distro(self, distro_root): ++ distro_bin = os.path.join(distro_root, 'bin') ++ # We don't want to pick up a possibly present lsb_release in the ++ # distro that runs this test, so we use a PATH with only one entry: ++ os.environ["PATH"] = distro_bin ++ distro._UNIXCONFDIR = os.path.join(distro_root, RELATIVE_UNIXCONFDIR) ++ ++ ++@pytest.mark.skipif(not IS_LINUX, reason='Irrelevant on non-linux') ++class TestOSRelease: ++ ++ def setup_method(self, test_method): ++ dist = test_method.__name__.split('_')[1] ++ os_release = os.path.join(DISTROS_DIR, dist, 'etc', 'os-release') ++ self.distro = distro.LinuxDistribution(False, os_release, 'non') ++ ++ def _test_outcome(self, outcome): ++ assert self.distro.id() == outcome.get('id', '') ++ assert self.distro.name() == outcome.get('name', '') ++ assert self.distro.name(pretty=True) == outcome.get('pretty_name', '') ++ assert self.distro.version() == outcome.get('version', '') ++ assert self.distro.version(pretty=True) == \ ++ outcome.get('pretty_version', '') ++ assert self.distro.version(best=True) == \ ++ outcome.get('best_version', '') ++ assert self.distro.like() == outcome.get('like', '') ++ assert self.distro.codename() == outcome.get('codename', '') ++ ++ def test_arch_os_release(self): ++ desired_outcome = { ++ 'id': 'arch', ++ 'name': 'Arch Linux', ++ 'pretty_name': 'Arch Linux', ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_kali_os_release(self): ++ desired_outcome = { ++ 'id': 'kali', ++ 'name': 'Kali GNU/Linux', ++ 'pretty_name': 'Kali GNU/Linux Rolling', ++ 'version': '2017.1', ++ 'pretty_version': '2017.1', ++ 'best_version': '2017.1', ++ 'like': 'debian' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_centos7_os_release(self): ++ desired_outcome = { ++ 'id': 'centos', ++ 'name': 'CentOS Linux', ++ 'pretty_name': 'CentOS Linux 7 (Core)', ++ 'version': '7', ++ 'pretty_version': '7 (Core)', ++ 'best_version': '7', ++ 'like': 'rhel fedora', ++ 'codename': 'Core' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_coreos_os_release(self): ++ desired_outcome = { ++ 'id': 'coreos', ++ 'name': 'CoreOS', ++ 'pretty_name': 'CoreOS 899.15.0', ++ 'version': '899.15.0', ++ 'pretty_version': '899.15.0', ++ 'best_version': '899.15.0' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_debian8_os_release(self): ++ desired_outcome = { ++ 'id': 'debian', ++ 'name': 'Debian GNU/Linux', ++ 'pretty_name': 'Debian GNU/Linux 8 (jessie)', ++ 'version': '8', ++ 'pretty_version': '8 (jessie)', ++ 'best_version': '8', ++ 'codename': 'jessie' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_fedora19_os_release(self): ++ desired_outcome = { ++ 'id': 'fedora', ++ 'name': 'Fedora', ++ 'pretty_name': u'Fedora 19 (Schr\u00F6dinger\u2019s Cat)', ++ 'version': '19', ++ 'pretty_version': u'19 (Schr\u00F6dinger\u2019s Cat)', ++ 'best_version': '19', ++ 'codename': u'Schr\u00F6dinger\u2019s Cat' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_fedora23_os_release(self): ++ desired_outcome = { ++ 'id': 'fedora', ++ 'name': 'Fedora', ++ 'pretty_name': 'Fedora 23 (Twenty Three)', ++ 'version': '23', ++ 'pretty_version': '23 (Twenty Three)', ++ 'best_version': '23', ++ 'codename': 'Twenty Three' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_fedora30_os_release(self): ++ # Fedora 21 and above no longer have code names but the metadata in os-release was only ++ # changed in a detectable way in Fedora 30+. The piece in parenthesis in the pretty_name ++ # field contains the VARIANT and differs depending on the variant which was installed. ++ desired_outcome = { ++ 'id': 'fedora', ++ 'name': 'Fedora', ++ 'pretty_name': 'Fedora 30 (Thirty)', ++ 'version': '30', ++ 'pretty_version': '30', ++ 'best_version': '30', ++ 'codename': '' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_kvmibm1_os_release(self): ++ desired_outcome = { ++ 'id': 'kvmibm', ++ 'name': 'KVM for IBM z Systems', ++ 'pretty_name': 'KVM for IBM z Systems 1.1.1 (Z)', ++ 'version': '1.1.1', ++ 'pretty_version': '1.1.1 (Z)', ++ 'best_version': '1.1.1', ++ 'like': 'rhel fedora', ++ 'codename': 'Z' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_linuxmint17_os_release(self): ++ # Note: LinuxMint 17 actually *does* have Ubuntu 14.04 data in its ++ # os-release file. See discussion in GitHub issue #78. ++ desired_outcome = { ++ 'id': 'ubuntu', ++ 'name': 'Ubuntu', ++ 'pretty_name': 'Ubuntu 14.04.3 LTS', ++ 'version': '14.04', ++ 'pretty_version': '14.04 (Trusty Tahr)', ++ 'best_version': '14.04.3', ++ 'like': 'debian', ++ 'codename': 'Trusty Tahr' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_mageia5_os_release(self): ++ desired_outcome = { ++ 'id': 'mageia', ++ 'name': 'Mageia', ++ 'pretty_name': 'Mageia 5', ++ 'version': '5', ++ 'pretty_version': '5', ++ 'best_version': '5', ++ 'like': 'mandriva fedora', ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_manjaro1512_os_release(self): ++ self._test_outcome({ ++ 'id': 'manjaro', ++ 'name': 'Manjaro Linux', ++ 'pretty_name': 'Manjaro Linux', ++ }) ++ ++ def test_opensuse42_os_release(self): ++ desired_outcome = { ++ 'id': 'opensuse', ++ 'name': 'openSUSE Leap', ++ 'pretty_name': 'openSUSE Leap 42.1 (x86_64)', ++ 'version': '42.1', ++ 'pretty_version': '42.1', ++ 'best_version': '42.1', ++ 'like': 'suse', ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_raspbian7_os_release(self): ++ desired_outcome = { ++ 'id': 'raspbian', ++ 'name': 'Raspbian GNU/Linux', ++ 'pretty_name': 'Raspbian GNU/Linux 7 (wheezy)', ++ 'version': '7', ++ 'pretty_version': '7 (wheezy)', ++ 'best_version': '7', ++ 'like': 'debian', ++ 'codename': 'wheezy' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_raspbian8_os_release(self): ++ desired_outcome = { ++ 'id': 'raspbian', ++ 'name': 'Raspbian GNU/Linux', ++ 'pretty_name': 'Raspbian GNU/Linux 8 (jessie)', ++ 'version': '8', ++ 'pretty_version': '8 (jessie)', ++ 'best_version': '8', ++ 'like': 'debian', ++ 'codename': 'jessie' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_rhel7_os_release(self): ++ desired_outcome = { ++ 'id': 'rhel', ++ 'name': 'Red Hat Enterprise Linux Server', ++ 'pretty_name': 'Red Hat Enterprise Linux Server 7.0 (Maipo)', ++ 'version': '7.0', ++ 'pretty_version': '7.0 (Maipo)', ++ 'best_version': '7.0', ++ 'like': 'fedora', ++ 'codename': 'Maipo' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_slackware14_os_release(self): ++ desired_outcome = { ++ 'id': 'slackware', ++ 'name': 'Slackware', ++ 'pretty_name': 'Slackware 14.1', ++ 'version': '14.1', ++ 'pretty_version': '14.1', ++ 'best_version': '14.1' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_sles12_os_release(self): ++ desired_outcome = { ++ 'id': 'sles', ++ 'name': 'SLES', ++ 'pretty_name': 'SUSE Linux Enterprise Server 12 SP1', ++ 'version': '12.1', ++ 'pretty_version': '12.1', ++ 'best_version': '12.1' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_ubuntu14_os_release(self): ++ desired_outcome = { ++ 'id': 'ubuntu', ++ 'name': 'Ubuntu', ++ 'pretty_name': 'Ubuntu 14.04.3 LTS', ++ 'version': '14.04', ++ 'pretty_version': '14.04 (Trusty Tahr)', ++ 'best_version': '14.04.3', ++ 'like': 'debian', ++ 'codename': 'Trusty Tahr' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_ubuntu16_os_release(self): ++ desired_outcome = { ++ 'id': 'ubuntu', ++ 'name': 'Ubuntu', ++ 'pretty_name': 'Ubuntu 16.04.1 LTS', ++ 'version': '16.04', ++ 'pretty_version': '16.04 (xenial)', ++ 'best_version': '16.04.1', ++ 'like': 'debian', ++ 'codename': 'xenial' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_amazon2016_os_release(self): ++ desired_outcome = { ++ 'id': 'amzn', ++ 'name': 'Amazon Linux AMI', ++ 'pretty_name': 'Amazon Linux AMI 2016.03', ++ 'version': '2016.03', ++ 'pretty_version': '2016.03', ++ 'best_version': '2016.03', ++ 'like': 'rhel fedora' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_scientific7_os_release(self): ++ desired_outcome = { ++ 'id': 'rhel', ++ 'name': 'Scientific Linux', ++ 'pretty_name': 'Scientific Linux 7.2 (Nitrogen)', ++ 'version': '7.2', ++ 'pretty_version': '7.2 (Nitrogen)', ++ 'best_version': '7.2', ++ 'like': 'fedora', ++ 'codename': 'Nitrogen' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_gentoo_os_release(self): ++ desired_outcome = { ++ 'id': 'gentoo', ++ 'name': 'Gentoo', ++ 'pretty_name': 'Gentoo/Linux', ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_openelec6_os_release(self): ++ desired_outcome = { ++ 'id': 'openelec', ++ 'name': 'OpenELEC', ++ 'pretty_name': 'OpenELEC (official) - Version: 6.0.3', ++ 'version': '6.0', ++ 'pretty_version': '6.0', ++ 'best_version': '6.0.3', ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_cloudlinux7_os_release(self): ++ desired_outcome = { ++ 'id': 'cloudlinux', ++ 'codename': 'Yury Malyshev', ++ 'name': 'CloudLinux', ++ 'pretty_name': 'CloudLinux 7.3 (Yury Malyshev)', ++ 'like': 'rhel fedora centos', ++ 'version': '7.3', ++ 'pretty_version': '7.3 (Yury Malyshev)', ++ 'best_version': '7.3', ++ 'major_version': '7', ++ 'minor_version': '3' ++ } ++ self._test_outcome(desired_outcome) ++ ++ ++@pytest.mark.skipif(not IS_LINUX, reason='Irrelevant on non-linux') ++class TestLSBRelease(DistroTestCase): ++ ++ def setup_method(self, test_method): ++ super(TestLSBRelease, self).setup_method(test_method) ++ dist = test_method.__name__.split('_')[1] ++ self._setup_for_distro(os.path.join(DISTROS_DIR, dist)) ++ self.distro = distro.LinuxDistribution(True, 'non', 'non') ++ ++ def _test_outcome(self, outcome): ++ assert self.distro.id() == outcome.get('id', '') ++ assert self.distro.name() == outcome.get('name', '') ++ assert self.distro.name(pretty=True) == outcome.get('pretty_name', '') ++ assert self.distro.version() == outcome.get('version', '') ++ assert self.distro.version(pretty=True) == \ ++ outcome.get('pretty_version', '') ++ assert self.distro.version(best=True) == \ ++ outcome.get('best_version', '') ++ assert self.distro.like() == outcome.get('like', '') ++ assert self.distro.codename() == outcome.get('codename', '') ++ ++ def test_linuxmint17_lsb_release(self): ++ desired_outcome = { ++ 'id': 'linuxmint', ++ 'name': 'LinuxMint', ++ 'pretty_name': 'Linux Mint 17.3 Rosa', ++ 'version': '17.3', ++ 'pretty_version': '17.3 (rosa)', ++ 'best_version': '17.3', ++ 'codename': 'rosa' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_manjaro1512_lsb_release(self): ++ self._test_outcome({ ++ 'id': 'manjarolinux', ++ 'name': 'ManjaroLinux', ++ 'pretty_name': 'Manjaro Linux', ++ 'version': '15.12', ++ 'pretty_version': '15.12 (Capella)', ++ 'best_version': '15.12', ++ 'codename': 'Capella' ++ }) ++ ++ # @pytest.mark.xfail ++ # def test_openelec6_lsb_release(self): ++ # # TODO: This should be fixed as part of #109 when dealing ++ # # with distro inconsistencies ++ # desired_outcome = { ++ # 'id': 'openelec', ++ # 'name': 'OpenELEC', ++ # 'pretty_name': 'OpenELEC (official) - Version: 6.0.3', ++ # 'version': '6.0.3', ++ # 'pretty_version': '6.0.3', ++ # 'best_version': '6.0.3', ++ # } ++ # self._test_outcome(desired_outcome) ++ ++ def test_openbsd62_uname(self): ++ self._test_outcome({ ++ 'id': 'openbsd', ++ 'name': 'OpenBSD', ++ 'version': '6.2', ++ 'pretty_name': 'OpenBSD 6.2', ++ 'pretty_version': '6.2', ++ 'best_version': '6.2' ++ }) ++ ++ def test_netbsd711_uname(self): ++ self._test_outcome({ ++ 'id': 'netbsd', ++ 'name': 'NetBSD', ++ 'version': '7.1.1', ++ 'pretty_name': 'NetBSD 7.1.1', ++ 'pretty_version': '7.1.1', ++ 'best_version': '7.1.1' ++ }) ++ ++ def test_freebsd111_uname(self): ++ self._test_outcome({ ++ 'id': 'freebsd', ++ 'name': 'FreeBSD', ++ 'version': '11.1', ++ 'pretty_name': 'FreeBSD 11.1', ++ 'pretty_version': '11.1', ++ 'best_version': '11.1' ++ }) ++ ++ def test_ubuntu14normal_lsb_release(self): ++ self._setup_for_distro(os.path.join(TESTDISTROS, 'lsb', ++ 'ubuntu14_normal')) ++ ++ self.distro = distro.LinuxDistribution(True, 'non', 'non') ++ ++ desired_outcome = { ++ 'id': 'ubuntu', ++ 'name': 'Ubuntu', ++ 'pretty_name': 'Ubuntu 14.04.3 LTS', ++ 'version': '14.04', ++ 'pretty_version': '14.04 (trusty)', ++ 'best_version': '14.04.3', ++ 'codename': 'trusty' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_ubuntu14nomodules_lsb_release(self): ++ self._setup_for_distro(os.path.join(TESTDISTROS, 'lsb', ++ 'ubuntu14_nomodules')) ++ ++ self.distro = distro.LinuxDistribution(True, 'non', 'non') ++ ++ desired_outcome = { ++ 'id': 'ubuntu', ++ 'name': 'Ubuntu', ++ 'pretty_name': 'Ubuntu 14.04.3 LTS', ++ 'version': '14.04', ++ 'pretty_version': '14.04 (trusty)', ++ 'best_version': '14.04.3', ++ 'codename': 'trusty' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_trailingblanks_lsb_release(self): ++ self._setup_for_distro(os.path.join(TESTDISTROS, 'lsb', ++ 'ubuntu14_trailingblanks')) ++ ++ self.distro = distro.LinuxDistribution(True, 'non', 'non') ++ ++ desired_outcome = { ++ 'id': 'ubuntu', ++ 'name': 'Ubuntu', ++ 'pretty_name': 'Ubuntu 14.04.3 LTS', ++ 'version': '14.04', ++ 'pretty_version': '14.04 (trusty)', ++ 'best_version': '14.04.3', ++ 'codename': 'trusty' ++ } ++ self._test_outcome(desired_outcome) ++ ++ @pytest.mark.parametrize('errnum', ('001', '002', '126', '130', '255')) ++ def test_lsb_release_error_level(self, errnum): ++ self._setup_for_distro(os.path.join( ++ TESTDISTROS, 'lsb', 'lsb_rc{0}'.format(errnum))) ++ with pytest.raises(subprocess.CalledProcessError) as excinfo: ++ distro.LinuxDistribution(True, 'non', 'non')._lsb_release_info ++ assert excinfo.value.returncode == int(errnum) ++ ++ ++@pytest.mark.skipif(not IS_LINUX, reason='Irrelevant on non-linux') ++class TestSpecialRelease(DistroTestCase): ++ def _test_outcome(self, outcome): ++ assert self.distro.id() == outcome.get('id', '') ++ assert self.distro.name() == outcome.get('name', '') ++ assert self.distro.name(pretty=True) == outcome.get('pretty_name', '') ++ assert self.distro.version() == outcome.get('version', '') ++ assert self.distro.version(pretty=True) == \ ++ outcome.get('pretty_version', '') ++ assert self.distro.version(best=True) == \ ++ outcome.get('best_version', '') ++ assert self.distro.like() == outcome.get('like', '') ++ assert self.distro.codename() == outcome.get('codename', '') ++ assert self.distro.major_version() == outcome.get('major_version', '') ++ assert self.distro.minor_version() == outcome.get('minor_version', '') ++ assert self.distro.build_number() == outcome.get('build_number', '') ++ ++ def test_empty_release(self): ++ distro_release = os.path.join(SPECIAL, 'empty-release') ++ self.distro = distro.LinuxDistribution(False, 'non', distro_release) ++ ++ desired_outcome = { ++ 'id': 'empty' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_unknowndistro_release(self): ++ self._setup_for_distro(os.path.join(TESTDISTROS, 'distro', ++ 'unknowndistro')) ++ ++ self.distro = distro.LinuxDistribution() ++ ++ desired_outcome = { ++ 'id': 'unknowndistro', ++ 'name': 'Unknown Distro', ++ 'pretty_name': 'Unknown Distro 1.0 (Unknown Codename)', ++ 'version': '1.0', ++ 'pretty_version': '1.0 (Unknown Codename)', ++ 'best_version': '1.0', ++ 'codename': 'Unknown Codename', ++ 'major_version': '1', ++ 'minor_version': '0' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_bad_uname(self): ++ self._setup_for_distro(os.path.join(TESTDISTROS, 'distro', ++ 'baduname')) ++ self.distro = distro.LinuxDistribution() ++ ++ assert self.distro.uname_attr('id') == '' ++ assert self.distro.uname_attr('name') == '' ++ assert self.distro.uname_attr('release') == '' ++ ++ ++@pytest.mark.skipif(not IS_LINUX, reason='Irrelevant on non-linux') ++class TestDistroRelease: ++ ++ def _test_outcome(self, ++ outcome, ++ distro_name='', ++ version='', ++ release_file_id='', ++ release_file_suffix='release'): ++ release_file_id = release_file_id or distro_name ++ distro_release = os.path.join( ++ DISTROS_DIR, distro_name + version, 'etc', '{0}-{1}'.format( ++ release_file_id, release_file_suffix)) ++ self.distro = distro.LinuxDistribution(False, 'non', distro_release) ++ ++ assert self.distro.id() == outcome.get('id', '') ++ assert self.distro.name() == outcome.get('name', '') ++ assert self.distro.name(pretty=True) == outcome.get('pretty_name', '') ++ assert self.distro.version() == outcome.get('version', '') ++ assert self.distro.version(pretty=True) == \ ++ outcome.get('pretty_version', '') ++ assert self.distro.version(best=True) == \ ++ outcome.get('best_version', '') ++ assert self.distro.like() == outcome.get('like', '') ++ assert self.distro.codename() == outcome.get('codename', '') ++ assert self.distro.major_version() == outcome.get('major_version', '') ++ assert self.distro.minor_version() == outcome.get('minor_version', '') ++ assert self.distro.build_number() == outcome.get('build_number', '') ++ ++ def test_arch_dist_release(self): ++ desired_outcome = { ++ 'id': 'arch' ++ } ++ self._test_outcome(desired_outcome, 'arch') ++ ++ def test_centos5_dist_release(self): ++ desired_outcome = { ++ 'id': 'centos', ++ 'name': 'CentOS', ++ 'pretty_name': 'CentOS 5.11 (Final)', ++ 'version': '5.11', ++ 'pretty_version': '5.11 (Final)', ++ 'best_version': '5.11', ++ 'codename': 'Final', ++ 'major_version': '5', ++ 'minor_version': '11' ++ } ++ self._test_outcome(desired_outcome, 'centos', '5') ++ ++ def test_centos7_dist_release(self): ++ desired_outcome = { ++ 'id': 'centos', ++ 'name': 'CentOS Linux', ++ 'pretty_name': 'CentOS Linux 7.1.1503 (Core)', ++ 'version': '7.1.1503', ++ 'pretty_version': '7.1.1503 (Core)', ++ 'best_version': '7.1.1503', ++ 'codename': 'Core', ++ 'major_version': '7', ++ 'minor_version': '1', ++ 'build_number': '1503' ++ } ++ self._test_outcome(desired_outcome, 'centos', '7') ++ ++ def test_fedora19_dist_release(self): ++ desired_outcome = { ++ 'id': 'fedora', ++ 'name': 'Fedora', ++ 'pretty_name': u'Fedora 19 (Schr\u00F6dinger\u2019s Cat)', ++ 'version': '19', ++ 'pretty_version': u'19 (Schr\u00F6dinger\u2019s Cat)', ++ 'best_version': '19', ++ 'codename': u'Schr\u00F6dinger\u2019s Cat', ++ 'major_version': '19' ++ } ++ self._test_outcome(desired_outcome, 'fedora', '19') ++ ++ def test_fedora23_dist_release(self): ++ desired_outcome = { ++ 'id': 'fedora', ++ 'name': 'Fedora', ++ 'pretty_name': 'Fedora 23 (Twenty Three)', ++ 'version': '23', ++ 'pretty_version': '23 (Twenty Three)', ++ 'best_version': '23', ++ 'codename': 'Twenty Three', ++ 'major_version': '23' ++ } ++ self._test_outcome(desired_outcome, 'fedora', '23') ++ ++ def test_fedora30_dist_release(self): ++ desired_outcome = { ++ 'id': 'fedora', ++ 'name': 'Fedora', ++ 'pretty_name': 'Fedora 30 (Thirty)', ++ 'version': '30', ++ 'pretty_version': '30 (Thirty)', ++ 'best_version': '30', ++ 'codename': 'Thirty', ++ 'major_version': '30' ++ } ++ self._test_outcome(desired_outcome, 'fedora', '30') ++ ++ def test_gentoo_dist_release(self): ++ desired_outcome = { ++ 'id': 'gentoo', ++ 'name': 'Gentoo Base System', ++ 'pretty_name': 'Gentoo Base System 2.2', ++ 'version': '2.2', ++ 'pretty_version': '2.2', ++ 'best_version': '2.2', ++ 'major_version': '2', ++ 'minor_version': '2', ++ } ++ self._test_outcome(desired_outcome, 'gentoo') ++ ++ def test_kvmibm1_dist_release(self): ++ desired_outcome = { ++ 'id': 'base', ++ 'name': 'KVM for IBM z Systems', ++ 'pretty_name': 'KVM for IBM z Systems 1.1.1 (Z)', ++ 'version': '1.1.1', ++ 'pretty_version': '1.1.1 (Z)', ++ 'best_version': '1.1.1', ++ 'codename': 'Z', ++ 'major_version': '1', ++ 'minor_version': '1', ++ 'build_number': '1' ++ } ++ self._test_outcome(desired_outcome, 'kvmibm', '1', 'base') ++ ++ def test_mageia5_dist_release(self): ++ desired_outcome = { ++ 'id': 'mageia', ++ 'name': 'Mageia', ++ 'pretty_name': 'Mageia 5 (Official)', ++ 'version': '5', ++ 'pretty_version': '5 (Official)', ++ 'best_version': '5', ++ 'codename': 'Official', ++ 'major_version': '5' ++ } ++ self._test_outcome(desired_outcome, 'mageia', '5') ++ ++ def test_manjaro1512_dist_release(self): ++ self._test_outcome({ ++ 'id': 'manjaro', ++ 'name': 'Manjaro Linux', ++ 'pretty_name': 'Manjaro Linux', ++ 'version': '', ++ 'codename': '' ++ }, 'manjaro', '1512') ++ ++ def test_opensuse42_dist_release(self): ++ desired_outcome = { ++ 'id': 'suse', ++ 'name': 'openSUSE', ++ 'pretty_name': 'openSUSE 42.1 (x86_64)', ++ 'version': '42.1', ++ 'pretty_version': '42.1 (x86_64)', ++ 'best_version': '42.1', ++ 'codename': 'x86_64', ++ 'major_version': '42', ++ 'minor_version': '1' ++ } ++ self._test_outcome(desired_outcome, 'opensuse', '42', 'SuSE') ++ ++ def test_oracle7_dist_release(self): ++ desired_outcome = { ++ 'id': 'oracle', ++ 'name': 'Oracle Linux Server', ++ 'pretty_name': 'Oracle Linux Server 7.5', ++ 'version': '7.5', ++ 'pretty_version': '7.5', ++ 'best_version': '7.5', ++ 'major_version': '7', ++ 'minor_version': '5' ++ } ++ self._test_outcome(desired_outcome, 'oracle', '7') ++ ++ def test_rhel6_dist_release(self): ++ desired_outcome = { ++ 'id': 'rhel', ++ 'name': 'Red Hat Enterprise Linux Server', ++ 'pretty_name': 'Red Hat Enterprise Linux Server 6.5 (Santiago)', ++ 'version': '6.5', ++ 'pretty_version': '6.5 (Santiago)', ++ 'best_version': '6.5', ++ 'codename': 'Santiago', ++ 'major_version': '6', ++ 'minor_version': '5' ++ } ++ self._test_outcome(desired_outcome, 'rhel', '6', 'redhat') ++ ++ def test_rhel7_dist_release(self): ++ desired_outcome = { ++ 'id': 'rhel', ++ 'name': 'Red Hat Enterprise Linux Server', ++ 'pretty_name': 'Red Hat Enterprise Linux Server 7.0 (Maipo)', ++ 'version': '7.0', ++ 'pretty_version': '7.0 (Maipo)', ++ 'best_version': '7.0', ++ 'codename': 'Maipo', ++ 'major_version': '7', ++ 'minor_version': '0' ++ } ++ self._test_outcome(desired_outcome, 'rhel', '7', 'redhat') ++ ++ def test_slackware14_dist_release(self): ++ desired_outcome = { ++ 'id': 'slackware', ++ 'name': 'Slackware', ++ 'pretty_name': 'Slackware 14.1', ++ 'version': '14.1', ++ 'pretty_version': '14.1', ++ 'best_version': '14.1', ++ 'major_version': '14', ++ 'minor_version': '1' ++ } ++ self._test_outcome( ++ desired_outcome, ++ 'slackware', ++ '14', ++ release_file_suffix='version') ++ ++ def test_sles12_dist_release(self): ++ desired_outcome = { ++ 'id': 'suse', ++ 'name': 'SUSE Linux Enterprise Server', ++ 'pretty_name': 'SUSE Linux Enterprise Server 12 (s390x)', ++ 'version': '12', ++ 'pretty_version': '12 (s390x)', ++ 'best_version': '12', ++ 'major_version': '12', ++ 'codename': 's390x' ++ } ++ self._test_outcome(desired_outcome, 'sles', '12', 'SuSE') ++ ++ def test_cloudlinux5_dist_release(self): ++ # Uses redhat-release only to get information. ++ # The id of 'rhel' can only be fixed with issue #109. ++ desired_outcome = { ++ 'id': 'cloudlinux', ++ 'codename': 'Vladislav Volkov', ++ 'name': 'CloudLinux Server', ++ 'pretty_name': 'CloudLinux Server 5.11 (Vladislav Volkov)', ++ 'version': '5.11', ++ 'pretty_version': '5.11 (Vladislav Volkov)', ++ 'best_version': '5.11', ++ 'major_version': '5', ++ 'minor_version': '11' ++ } ++ self._test_outcome(desired_outcome, 'cloudlinux', '5', 'redhat') ++ ++ def test_cloudlinux6_dist_release(self): ++ # Same as above, only has redhat-release. ++ desired_outcome = { ++ 'id': 'cloudlinux', ++ 'codename': 'Oleg Makarov', ++ 'name': 'CloudLinux Server', ++ 'pretty_name': 'CloudLinux Server 6.8 (Oleg Makarov)', ++ 'version': '6.8', ++ 'pretty_version': '6.8 (Oleg Makarov)', ++ 'best_version': '6.8', ++ 'major_version': '6', ++ 'minor_version': '8' ++ } ++ self._test_outcome(desired_outcome, 'cloudlinux', '6', 'redhat') ++ ++ def test_cloudlinux7_dist_release(self): ++ desired_outcome = { ++ 'id': 'cloudlinux', ++ 'codename': 'Yury Malyshev', ++ 'name': 'CloudLinux', ++ 'pretty_name': 'CloudLinux 7.3 (Yury Malyshev)', ++ 'version': '7.3', ++ 'pretty_version': '7.3 (Yury Malyshev)', ++ 'best_version': '7.3', ++ 'major_version': '7', ++ 'minor_version': '3' ++ } ++ self._test_outcome(desired_outcome, 'cloudlinux', '7', 'redhat') ++ ++ ++@pytest.mark.skipif(not IS_LINUX, reason='Irrelevant on non-linux') ++class TestOverall(DistroTestCase): ++ """Test a LinuxDistribution object created with default arguments. ++ ++ The direct accessor functions on that object are tested (e.g. `id()`); they ++ implement the precedence between the different sources of information. ++ ++ In addition, because the distro release file is searched when not ++ specified, the information resulting from the distro release file is also ++ tested. The LSB and os-release sources are not tested again, because their ++ test is already done in TestLSBRelease and TestOSRelease, and their ++ algorithm does not depend on whether or not the file is specified. ++ ++ TODO: This class should have testcases for all distros that are claimed ++ to be reliably maintained w.r.t. to their ID (see `id()`). Testcases for ++ the following distros are still missing: ++ * `amazon` - Amazon Linux ++ * `gentoo` - GenToo Linux ++ * `ibm_powerkvm` - IBM PowerKVM ++ * `parallels` - Parallels ++ * `pidora` - Pidora (Fedora remix for Raspberry Pi) ++ * `raspbian` - Raspbian ++ * `scientific` - Scientific Linux ++ * `xenserver` - XenServer ++ """ ++ ++ def setup_method(self, test_method): ++ super(TestOverall, self).setup_method(test_method) ++ dist = test_method.__name__.split('_')[1] ++ self._setup_for_distro(os.path.join(DISTROS_DIR, dist)) ++ self.distro = distro.LinuxDistribution() ++ ++ def _test_outcome(self, outcome): ++ assert self.distro.id() == outcome.get('id', '') ++ assert self.distro.name() == outcome.get('name', '') ++ assert self.distro.name(pretty=True) == outcome.get('pretty_name', '') ++ assert self.distro.version() == outcome.get('version', '') ++ assert self.distro.version(pretty=True) == \ ++ outcome.get('pretty_version', '') ++ assert self.distro.version(best=True) == \ ++ outcome.get('best_version', '') ++ assert self.distro.like() == outcome.get('like', '') ++ assert self.distro.codename() == outcome.get('codename', '') ++ assert self.distro.major_version() == outcome.get('major_version', '') ++ assert self.distro.minor_version() == outcome.get('minor_version', '') ++ assert self.distro.build_number() == outcome.get('build_number', '') ++ ++ def _test_non_existing_release_file(self): ++ # Test the info from the searched distro release file ++ # does not have one. ++ assert self.distro.distro_release_file == '' ++ assert len(self.distro.distro_release_info()) == 0 ++ ++ def _test_release_file_info(self, filename, outcome): ++ # Test the info from the searched distro release file ++ assert os.path.basename(self.distro.distro_release_file) == filename ++ distro_info = self.distro.distro_release_info() ++ for key, value in outcome.items(): ++ assert distro_info[key] == value ++ return distro_info ++ ++ def test_arch_release(self): ++ desired_outcome = { ++ 'id': 'arch', ++ 'name': 'Arch Linux', ++ 'pretty_name': 'Arch Linux', ++ } ++ self._test_outcome(desired_outcome) ++ ++ # Test the info from the searched distro release file ++ # Does not have one; The empty /etc/arch-release file is not ++ # considered a valid distro release file: ++ self._test_non_existing_release_file() ++ ++ def test_centos5_release(self): ++ desired_outcome = { ++ 'id': 'centos', ++ 'name': 'CentOS', ++ 'pretty_name': 'CentOS 5.11 (Final)', ++ 'version': '5.11', ++ 'pretty_version': '5.11 (Final)', ++ 'best_version': '5.11', ++ 'codename': 'Final', ++ 'major_version': '5', ++ 'minor_version': '11' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'centos', ++ 'name': 'CentOS', ++ 'version_id': '5.11', ++ 'codename': 'Final' ++ } ++ self._test_release_file_info('centos-release', desired_info) ++ ++ def test_centos7_release(self): ++ desired_outcome = { ++ 'id': 'centos', ++ 'name': 'CentOS Linux', ++ 'pretty_name': 'CentOS Linux 7 (Core)', ++ 'version': '7', ++ 'pretty_version': '7 (Core)', ++ 'best_version': '7.1.1503', ++ 'like': 'rhel fedora', ++ 'codename': 'Core', ++ 'major_version': '7' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'centos', ++ 'name': 'CentOS Linux', ++ 'version_id': '7.1.1503', ++ 'codename': 'Core' ++ } ++ self._test_release_file_info('centos-release', desired_info) ++ ++ def test_coreos_release(self): ++ desired_outcome = { ++ 'id': 'coreos', ++ 'name': 'CoreOS', ++ 'pretty_name': 'CoreOS 899.15.0', ++ 'version': '899.15.0', ++ 'pretty_version': '899.15.0', ++ 'best_version': '899.15.0', ++ 'major_version': '899', ++ 'minor_version': '15', ++ 'build_number': '0' ++ } ++ self._test_outcome(desired_outcome) ++ self._test_non_existing_release_file() ++ ++ def test_debian8_release(self): ++ desired_outcome = { ++ 'id': 'debian', ++ 'name': 'Debian GNU/Linux', ++ 'pretty_name': 'Debian GNU/Linux 8 (jessie)', ++ 'version': '8', ++ 'pretty_version': '8 (jessie)', ++ 'best_version': '8.2', ++ 'codename': 'jessie', ++ 'major_version': '8' ++ } ++ self._test_outcome(desired_outcome) ++ self._test_non_existing_release_file() ++ ++ def test_exherbo_release(self): ++ desired_outcome = { ++ 'id': 'exherbo', ++ 'name': 'Exherbo', ++ 'pretty_name': 'Exherbo Linux', ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_fedora19_release(self): ++ desired_outcome = { ++ 'id': 'fedora', ++ 'name': 'Fedora', ++ 'pretty_name': u'Fedora 19 (Schr\u00F6dinger\u2019s Cat)', ++ 'version': '19', ++ 'pretty_version': u'19 (Schr\u00F6dinger\u2019s Cat)', ++ 'best_version': '19', ++ 'codename': u'Schr\u00F6dinger\u2019s Cat', ++ 'major_version': '19' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'fedora', ++ 'name': 'Fedora', ++ 'version_id': '19', ++ 'codename': u'Schr\u00F6dinger\u2019s Cat' ++ } ++ self._test_release_file_info('fedora-release', desired_info) ++ ++ def test_fedora23_release(self): ++ desired_outcome = { ++ 'id': 'fedora', ++ 'name': 'Fedora', ++ 'pretty_name': 'Fedora 23 (Twenty Three)', ++ 'version': '23', ++ 'pretty_version': '23 (Twenty Three)', ++ 'best_version': '23', ++ 'codename': 'Twenty Three', ++ 'major_version': '23' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'fedora', ++ 'name': 'Fedora', ++ 'version_id': '23', ++ 'codename': 'Twenty Three' ++ } ++ self._test_release_file_info('fedora-release', desired_info) ++ ++ def test_fedora30_release(self): ++ desired_outcome = { ++ 'id': 'fedora', ++ 'name': 'Fedora', ++ 'pretty_name': 'Fedora 30 (Thirty)', ++ 'version': '30', ++ 'pretty_version': '30', ++ 'best_version': '30', ++ 'codename': '', ++ 'major_version': '30' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'fedora', ++ 'name': 'Fedora', ++ 'version_id': '30', ++ 'codename': 'Thirty' ++ } ++ self._test_release_file_info('fedora-release', desired_info) ++ ++ def test_kvmibm1_release(self): ++ desired_outcome = { ++ 'id': 'kvmibm', ++ 'name': 'KVM for IBM z Systems', ++ 'pretty_name': 'KVM for IBM z Systems 1.1.1 (Z)', ++ 'version': '1.1.1', ++ 'pretty_version': '1.1.1 (Z)', ++ 'best_version': '1.1.1', ++ 'like': 'rhel fedora', ++ 'codename': 'Z', ++ 'major_version': '1', ++ 'minor_version': '1', ++ 'build_number': '1' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'base', ++ 'name': 'KVM for IBM z Systems', ++ 'version_id': '1.1.1', ++ 'codename': 'Z' ++ } ++ self._test_release_file_info('base-release', desired_info) ++ ++ def test_linuxmint17_release(self): ++ desired_outcome = { ++ 'id': 'ubuntu', ++ 'name': 'Ubuntu', ++ 'pretty_name': 'Ubuntu 14.04.3 LTS', ++ 'version': '14.04', ++ 'pretty_version': '14.04 (Trusty Tahr)', ++ 'best_version': '14.04.3', ++ 'like': 'debian', ++ 'codename': 'Trusty Tahr', ++ 'major_version': '14', ++ 'minor_version': '04' ++ } ++ self._test_outcome(desired_outcome) ++ self._test_non_existing_release_file() ++ ++ def test_mageia5_release(self): ++ desired_outcome = { ++ 'id': 'mageia', ++ 'name': 'Mageia', ++ 'pretty_name': 'Mageia 5', ++ 'version': '5', ++ 'pretty_version': '5 (thornicroft)', ++ 'best_version': '5', ++ 'like': 'mandriva fedora', ++ # TODO: Codename differs between distro release and lsb_release. ++ 'codename': 'thornicroft', ++ 'major_version': '5' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'mageia', ++ 'name': 'Mageia', ++ 'version_id': '5', ++ 'codename': 'Official' ++ } ++ self._test_release_file_info('mageia-release', desired_info) ++ ++ def test_manjaro1512_release(self): ++ self._test_outcome({ ++ 'id': 'manjaro', ++ 'name': 'Manjaro Linux', ++ 'pretty_name': 'Manjaro Linux', ++ 'version': '15.12', ++ 'pretty_version': '15.12 (Capella)', ++ 'best_version': '15.12', ++ 'major_version': '15', ++ 'minor_version': '12', ++ 'codename': 'Capella' ++ }) ++ ++ self._test_release_file_info( ++ 'manjaro-release', ++ {'id': 'manjaro', ++ 'name': 'Manjaro Linux'}) ++ ++ def test_opensuse42_release(self): ++ desired_outcome = { ++ 'id': 'opensuse', ++ 'name': 'openSUSE Leap', ++ 'pretty_name': 'openSUSE Leap 42.1 (x86_64)', ++ 'version': '42.1', ++ 'pretty_version': '42.1 (x86_64)', ++ 'best_version': '42.1', ++ 'like': 'suse', ++ 'codename': 'x86_64', ++ 'major_version': '42', ++ 'minor_version': '1' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'SuSE', ++ 'name': 'openSUSE', ++ 'version_id': '42.1', ++ 'codename': 'x86_64' ++ } ++ self._test_release_file_info('SuSE-release', desired_info) ++ ++ def test_oracle7_release(self): ++ desired_outcome = { ++ 'id': 'oracle', ++ 'name': 'Oracle Linux Server', ++ 'pretty_name': 'Oracle Linux Server 7.5', ++ 'version': '7.5', ++ 'pretty_version': '7.5', ++ 'best_version': '7.5', ++ 'major_version': '7', ++ 'minor_version': '5' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'oracle', ++ 'name': 'Oracle Linux Server', ++ 'version_id': '7.5', ++ } ++ distro_info = self._test_release_file_info( ++ 'oracle-release', desired_info) ++ assert 'codename' not in distro_info ++ ++ def test_raspbian7_release(self): ++ desired_outcome = { ++ 'id': 'raspbian', ++ 'name': 'Raspbian GNU/Linux', ++ 'pretty_name': 'Raspbian GNU/Linux 7 (wheezy)', ++ 'version': '7', ++ 'pretty_version': '7 (wheezy)', ++ 'best_version': '7', ++ 'like': 'debian', ++ 'codename': 'wheezy', ++ 'major_version': '7', ++ } ++ self._test_outcome(desired_outcome) ++ self._test_non_existing_release_file() ++ ++ def test_raspbian8_release(self): ++ desired_outcome = { ++ 'id': 'raspbian', ++ 'name': 'Raspbian GNU/Linux', ++ 'pretty_name': 'Raspbian GNU/Linux 8 (jessie)', ++ 'version': '8', ++ 'pretty_version': '8 (jessie)', ++ 'best_version': '8', ++ 'like': 'debian', ++ 'codename': 'jessie', ++ 'major_version': '8', ++ } ++ self._test_outcome(desired_outcome) ++ self._test_non_existing_release_file() ++ ++ def test_rhel5_release(self): ++ desired_outcome = { ++ 'id': 'rhel', ++ 'name': 'Red Hat Enterprise Linux Server', ++ 'pretty_name': 'Red Hat Enterprise Linux Server 5.11 (Tikanga)', ++ 'version': '5.11', ++ 'pretty_version': '5.11 (Tikanga)', ++ 'best_version': '5.11', ++ 'codename': 'Tikanga', ++ 'major_version': '5', ++ 'minor_version': '11' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'redhat', ++ 'name': 'Red Hat Enterprise Linux Server', ++ 'version_id': '5.11', ++ 'codename': 'Tikanga' ++ } ++ self._test_release_file_info('redhat-release', desired_info) ++ ++ def test_rhel6_release(self): ++ desired_outcome = { ++ 'id': 'rhel', ++ 'name': 'Red Hat Enterprise Linux Server', ++ 'pretty_name': 'Red Hat Enterprise Linux Server 6.5 (Santiago)', ++ 'version': '6.5', ++ 'pretty_version': '6.5 (Santiago)', ++ 'best_version': '6.5', ++ 'codename': 'Santiago', ++ 'major_version': '6', ++ 'minor_version': '5' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'redhat', ++ 'name': 'Red Hat Enterprise Linux Server', ++ 'version_id': '6.5', ++ 'codename': 'Santiago' ++ } ++ self._test_release_file_info('redhat-release', desired_info) ++ ++ def test_rhel7_release(self): ++ desired_outcome = { ++ 'id': 'rhel', ++ 'name': 'Red Hat Enterprise Linux Server', ++ 'pretty_name': 'Red Hat Enterprise Linux Server 7.0 (Maipo)', ++ 'version': '7.0', ++ 'pretty_version': '7.0 (Maipo)', ++ 'best_version': '7.0', ++ 'like': 'fedora', ++ 'codename': 'Maipo', ++ 'major_version': '7', ++ 'minor_version': '0' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'redhat', ++ 'name': 'Red Hat Enterprise Linux Server', ++ 'version_id': '7.0', ++ 'codename': 'Maipo' ++ } ++ self._test_release_file_info('redhat-release', desired_info) ++ ++ def test_slackware14_release(self): ++ desired_outcome = { ++ 'id': 'slackware', ++ 'name': 'Slackware', ++ 'pretty_name': 'Slackware 14.1', ++ 'version': '14.1', ++ 'pretty_version': '14.1', ++ 'best_version': '14.1', ++ 'major_version': '14', ++ 'minor_version': '1' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'slackware', ++ 'name': 'Slackware', ++ 'version_id': '14.1', ++ } ++ distro_info = self._test_release_file_info( ++ 'slackware-version', desired_info) ++ assert 'codename' not in distro_info ++ ++ def test_sles12_release(self): ++ desired_outcome = { ++ 'id': 'sles', ++ 'name': 'SLES', ++ 'pretty_name': 'SUSE Linux Enterprise Server 12 SP1', ++ 'version': '12.1', ++ 'pretty_version': '12.1 (n/a)', ++ 'best_version': '12.1', ++ 'codename': 'n/a', ++ 'major_version': '12', ++ 'minor_version': '1' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'SuSE', ++ 'name': 'SUSE Linux Enterprise Server', ++ 'version_id': '12', ++ 'codename': 's390x' ++ } ++ self._test_release_file_info('SuSE-release', desired_info) ++ ++ def test_ubuntu14_release(self): ++ desired_outcome = { ++ 'id': 'ubuntu', ++ 'name': 'Ubuntu', ++ 'pretty_name': 'Ubuntu 14.04.3 LTS', ++ 'version': '14.04', ++ 'pretty_version': '14.04 (Trusty Tahr)', ++ 'best_version': '14.04.3', ++ 'like': 'debian', ++ 'codename': 'Trusty Tahr', ++ 'major_version': '14', ++ 'minor_version': '04' ++ } ++ self._test_outcome(desired_outcome) ++ ++ # Test the info from the searched distro release file ++ # Does not have one; /etc/debian_version is not considered a distro ++ # release file: ++ self._test_non_existing_release_file() ++ ++ def test_ubuntu16_release(self): ++ desired_outcome = { ++ 'id': 'ubuntu', ++ 'name': 'Ubuntu', ++ 'pretty_name': 'Ubuntu 16.04.1 LTS', ++ 'version': '16.04', ++ 'pretty_version': '16.04 (xenial)', ++ 'best_version': '16.04.1', ++ 'like': 'debian', ++ 'codename': 'xenial', ++ 'major_version': '16', ++ 'minor_version': '04' ++ } ++ self._test_outcome(desired_outcome) ++ ++ # Test the info from the searched distro release file ++ # Does not have one; /etc/debian_version is not considered a distro ++ # release file: ++ self._test_non_existing_release_file() ++ ++ def test_amazon2016_release(self): ++ desired_outcome = { ++ 'id': 'amzn', ++ 'name': 'Amazon Linux AMI', ++ 'pretty_name': 'Amazon Linux AMI 2016.03', ++ 'version': '2016.03', ++ 'pretty_version': '2016.03', ++ 'best_version': '2016.03', ++ 'like': 'rhel fedora', ++ 'major_version': '2016', ++ 'minor_version': '03' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_amazon2014_release(self): ++ # Amazon Linux 2014 only contains a system-release file. ++ # distro doesn't currently handle it. ++ desired_outcome = {} ++ self._test_outcome(desired_outcome) ++ ++ def test_scientific6_release(self): ++ desired_outcome = { ++ 'id': 'rhel', ++ 'name': 'Scientific Linux', ++ 'pretty_name': 'Scientific Linux 6.4 (Carbon)', ++ 'version': '6.4', ++ 'pretty_version': '6.4 (Carbon)', ++ 'best_version': '6.4', ++ 'codename': 'Carbon', ++ 'major_version': '6', ++ 'minor_version': '4', ++ ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'redhat', ++ 'name': 'Scientific Linux', ++ 'version_id': '6.4', ++ 'codename': 'Carbon' ++ } ++ self._test_release_file_info('redhat-release', desired_info) ++ ++ def test_scientific7_release(self): ++ desired_outcome = { ++ 'id': 'rhel', ++ 'name': 'Scientific Linux', ++ 'pretty_name': 'Scientific Linux 7.2 (Nitrogen)', ++ 'version': '7.2', ++ 'pretty_version': '7.2 (Nitrogen)', ++ 'best_version': '7.2', ++ 'like': 'fedora', ++ 'codename': 'Nitrogen', ++ 'major_version': '7', ++ 'minor_version': '2', ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'redhat', ++ 'name': 'Scientific Linux', ++ 'version_id': '7.2', ++ 'codename': 'Nitrogen' ++ } ++ self._test_release_file_info('redhat-release', desired_info) ++ ++ def test_gentoo_release(self): ++ desired_outcome = { ++ 'id': 'gentoo', ++ 'name': 'Gentoo', ++ 'pretty_name': 'Gentoo/Linux', ++ 'version': '2.2', ++ 'pretty_version': '2.2', ++ 'best_version': '2.2', ++ 'major_version': '2', ++ 'minor_version': '2', ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'gentoo', ++ 'name': 'Gentoo Base System', ++ 'version_id': '2.2', ++ } ++ self._test_release_file_info('gentoo-release', desired_info) ++ ++ def test_openelec6_release(self): ++ desired_outcome = { ++ 'id': 'openelec', ++ 'name': 'OpenELEC', ++ 'pretty_name': 'OpenELEC (official) - Version: 6.0.3', ++ 'version': '6.0', ++ 'pretty_version': '6.0', ++ 'best_version': '6.0.3', ++ 'major_version': '6', ++ 'minor_version': '0', ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_mandriva2011_release(self): ++ desired_outcome = { ++ 'id': 'mandrivalinux', ++ 'name': 'MandrivaLinux', ++ 'pretty_name': 'Mandriva Linux 2011.0', ++ 'version': '2011.0', ++ 'pretty_version': '2011.0 (turtle)', ++ 'best_version': '2011.0', ++ 'major_version': '2011', ++ 'minor_version': '0', ++ 'codename': 'turtle' ++ } ++ self._test_outcome(desired_outcome) ++ ++ desired_info = { ++ 'id': 'mandrake', ++ 'name': 'Mandriva Linux', ++ 'version_id': '2011.0', ++ } ++ self._test_release_file_info('mandrake-release', desired_info) ++ ++ def test_cloudlinux5_release(self): ++ # Uses redhat-release only to get information. ++ # The id of 'rhel' can only be fixed with issue #109. ++ desired_outcome = { ++ 'id': 'cloudlinux', ++ 'codename': 'Vladislav Volkov', ++ 'name': 'CloudLinux Server', ++ 'pretty_name': 'CloudLinux Server 5.11 (Vladislav Volkov)', ++ 'version': '5.11', ++ 'pretty_version': '5.11 (Vladislav Volkov)', ++ 'best_version': '5.11', ++ 'major_version': '5', ++ 'minor_version': '11' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_cloudlinux6_release(self): ++ # Same as above, only has redhat-release. ++ desired_outcome = { ++ 'id': 'cloudlinux', ++ 'codename': 'Oleg Makarov', ++ 'name': 'CloudLinux Server', ++ 'pretty_name': 'CloudLinux Server 6.8 (Oleg Makarov)', ++ 'version': '6.8', ++ 'pretty_version': '6.8 (Oleg Makarov)', ++ 'best_version': '6.8', ++ 'major_version': '6', ++ 'minor_version': '8' ++ } ++ self._test_outcome(desired_outcome) ++ ++ def test_cloudlinux7_release(self): ++ desired_outcome = { ++ 'id': 'cloudlinux', ++ 'codename': 'Yury Malyshev', ++ 'name': 'CloudLinux', ++ 'pretty_name': 'CloudLinux 7.3 (Yury Malyshev)', ++ 'like': 'rhel fedora centos', ++ 'version': '7.3', ++ 'pretty_version': '7.3 (Yury Malyshev)', ++ 'best_version': '7.3', ++ 'major_version': '7', ++ 'minor_version': '3' ++ } ++ self._test_outcome(desired_outcome) ++ ++ ++def _bad_os_listdir(path='.'): ++ """ This function is used by TestOverallWithEtcNotReadable to simulate ++ a folder that cannot be called with os.listdir() but files are still ++ readable. Forces distro to guess which *-release files are available. """ ++ raise OSError() ++ ++ ++@pytest.mark.skipIf(not IS_LINUX, reason='Irrelevant on non-linx') ++class TestOverallWithEtcNotReadable(TestOverall): ++ def setup_method(self, test_method): ++ self._old_listdir = os.listdir ++ os.listdir = _bad_os_listdir ++ super(TestOverallWithEtcNotReadable, self).setup_method(test_method) ++ ++ def teardown_method(self, test_method): ++ super(TestOverallWithEtcNotReadable, self).teardown_method(test_method) ++ if os.listdir is _bad_os_listdir: ++ os.listdir = self._old_listdir ++ ++ ++@pytest.mark.skipif(not IS_LINUX, reason='Irrelevant on non-linux') ++class TestGetAttr(DistroTestCase): ++ """Test the consistency between the results of ++ `{source}_release_attr()` and `{source}_release_info()` for all ++ distros in `DISTROS`. ++ """ ++ ++ def _test_attr(self, info_method, attr_method): ++ for dist in DISTROS: ++ self._setup_for_distro(os.path.join(DISTROS_DIR, dist)) ++ _distro = distro.LinuxDistribution() ++ info = getattr(_distro, info_method)() ++ for key in info.keys(): ++ try: ++ assert info[key] == getattr(_distro, attr_method)(key) ++ except AssertionError: ++ print("distro: {0}, key: {1}".format(dist, key)) ++ ++ def test_os_release_attr(self): ++ self._test_attr('os_release_info', 'os_release_attr') ++ ++ def test_lsb_release_attr(self): ++ self._test_attr('lsb_release_info', 'lsb_release_attr') ++ ++ def test_distro_release_attr(self): ++ self._test_attr('distro_release_info', 'distro_release_attr') ++ ++ ++@pytest.mark.skipif(not IS_LINUX, reason='Irrelevant on non-linux') ++class TestInfo(DistroTestCase): ++ ++ def setup_method(self, test_method): ++ super(TestInfo, self).setup_method(test_method) ++ self.ubuntu14_os_release = os.path.join( ++ DISTROS_DIR, 'ubuntu14', 'etc', 'os-release') ++ ++ def test_info(self): ++ _distro = distro.LinuxDistribution( ++ False, self.ubuntu14_os_release, 'non') ++ ++ desired_info = { ++ 'id': 'ubuntu', ++ 'version': '14.04', ++ 'like': 'debian', ++ 'version_parts': { ++ 'major': '14', ++ 'minor': '04', ++ 'build_number': '' ++ }, ++ 'codename': 'Trusty Tahr' ++ } ++ ++ info = _distro.info() ++ assert info == desired_info ++ ++ desired_info_diff = { ++ 'version': '14.04 (Trusty Tahr)' ++ } ++ desired_info.update(desired_info_diff) ++ info = _distro.info(pretty=True) ++ assert info == desired_info ++ ++ desired_info_diff = { ++ 'version': '14.04.3', ++ 'version_parts': { ++ 'major': '14', ++ 'minor': '04', ++ 'build_number': '3' ++ } ++ } ++ desired_info.update(desired_info_diff) ++ info = _distro.info(best=True) ++ assert info == desired_info ++ ++ desired_info_diff = { ++ 'version': '14.04.3 (Trusty Tahr)' ++ } ++ desired_info.update(desired_info_diff) ++ info = _distro.info(pretty=True, best=True) ++ assert info == desired_info ++ ++ def test_none(self): ++ ++ def _test_none(info): ++ assert info['id'] == '' ++ assert info['version'] == '' ++ assert info['like'] == '' ++ assert info['version_parts']['major'] == '' ++ assert info['version_parts']['minor'] == '' ++ assert info['version_parts']['build_number'] == '' ++ assert info['codename'] == '' ++ ++ _distro = distro.LinuxDistribution(False, 'non', 'non') ++ ++ info = _distro.info() ++ _test_none(info) ++ ++ info = _distro.info(best=True) ++ _test_none(info) ++ ++ info = _distro.info(pretty=True) ++ _test_none(info) ++ ++ info = _distro.info(pretty=True, best=True) ++ _test_none(info) ++ ++ def test_linux_distribution(self): ++ _distro = distro.LinuxDistribution(False, self.ubuntu14_os_release) ++ i = _distro.linux_distribution() ++ assert i == ('Ubuntu', '14.04', 'Trusty Tahr') ++ ++ def test_linux_distribution_full_false(self): ++ _distro = distro.LinuxDistribution(False, self.ubuntu14_os_release) ++ i = _distro.linux_distribution(full_distribution_name=False) ++ assert i == ('ubuntu', '14.04', 'Trusty Tahr') ++ ++ def test_all(self): ++ """Test info() by comparing its results with the results of specific ++ consolidated accessor functions. ++ """ ++ def _test_all(info, best=False, pretty=False): ++ assert info['id'] == _distro.id() ++ assert info['version'] == _distro.version(pretty=pretty, best=best) ++ assert info['version_parts']['major'] == \ ++ _distro.major_version(best=best) ++ assert info['version_parts']['minor'] == \ ++ _distro.minor_version(best=best) ++ assert info['version_parts']['build_number'] == \ ++ _distro.build_number(best=best) ++ assert info['like'] == _distro.like() ++ assert info['codename'] == _distro.codename() ++ assert len(info['version_parts']) == 3 ++ assert len(info) == 5 ++ ++ for dist in DISTROS: ++ self._setup_for_distro(os.path.join(DISTROS_DIR, dist)) ++ ++ _distro = distro.LinuxDistribution() ++ ++ info = _distro.info() ++ _test_all(info) ++ ++ info = _distro.info(best=True) ++ _test_all(info, best=True) ++ ++ info = _distro.info(pretty=True) ++ _test_all(info, pretty=True) ++ ++ info = _distro.info(pretty=True, best=True) ++ _test_all(info, pretty=True, best=True) ++ ++ ++@pytest.mark.skipif(not IS_LINUX, reason='Irrelevant on non-linux') ++class TestOSReleaseParsing: ++ """Test the parsing of os-release files. ++ """ ++ ++ def setup_method(self, test_method): ++ self.distro = distro.LinuxDistribution(False, None, None) ++ self.distro.debug = True ++ ++ def _get_props(self, input): ++ return self.distro._parse_os_release_content(StringIO( ++ input, ++ )) ++ ++ def _test_zero_length_props(self, input): ++ props = self._get_props(input) ++ assert len(props) == 0 ++ ++ def _test_empty_value(self, input): ++ props = self._get_props(input) ++ assert props.get('key', None) == '' ++ ++ def _test_parsed_value(self, input): ++ props = self._get_props(input) ++ assert props.get('key', None) == 'value' ++ ++ def test_kv_01_empty_file(self): ++ self._test_zero_length_props('') ++ ++ def test_kv_02_empty_line(self): ++ self._test_zero_length_props('\n') ++ ++ def test_kv_03_empty_line_with_crlf(self): ++ self._test_zero_length_props('\r\n') ++ ++ def test_kv_04_empty_line_with_just_cr(self): ++ self._test_zero_length_props('\r') ++ ++ def test_kv_05_comment(self): ++ self._test_zero_length_props('# KEY=value\n') ++ ++ def test_kv_06_empty_value(self): ++ self._test_empty_value('KEY=\n') ++ ++ def test_kv_07_empty_value_single_quoted(self): ++ self._test_empty_value('KEY=\'\'\n') ++ ++ def test_kv_08_empty_value_double_quoted(self): ++ self._test_empty_value('KEY=""\n') ++ ++ def test_kv_09_word(self): ++ self._test_parsed_value('KEY=value\n') ++ ++ def test_kv_10_word_no_newline(self): ++ self._test_parsed_value('KEY=value') ++ ++ def test_kv_11_word_with_crlf(self): ++ self._test_parsed_value('KEY=value\r\n') ++ ++ def test_kv_12_word_with_just_cr(self): ++ self._test_parsed_value('KEY=value\r') ++ ++ def test_kv_13_word_with_multi_blanks(self): ++ self._test_empty_value('KEY= cmd \n') ++ # Note: Without quotes, this assigns the empty string, and 'cmd' is ++ # a separate token that is being ignored (it would be a command ++ # in the shell). ++ ++ def test_kv_14_unquoted_words(self): ++ self._test_parsed_value('KEY=value cmd\n') ++ ++ def test_kv_15_double_quoted_words(self): ++ props = self._get_props('KEY="a simple value" cmd\n') ++ assert props.get('key', None) == 'a simple value' ++ ++ def test_kv_16_double_quoted_words_with_multi_blanks(self): ++ props = self._get_props('KEY=" a simple value "\n') ++ assert props.get('key', None) == ' a simple value ' ++ ++ def test_kv_17_double_quoted_word_with_single_quote(self): ++ props = self._get_props('KEY="it\'s value"\n') ++ assert props.get('key', None) == 'it\'s value' ++ ++ def test_kv_18_double_quoted_word_with_double_quote(self): ++ props = self._get_props('KEY="a \\"bold\\" move"\n') ++ assert props.get('key', None) == 'a "bold" move' ++ ++ def test_kv_19_single_quoted_words(self): ++ props = self._get_props('KEY=\'a simple value\'\n') ++ assert props.get('key', None) == 'a simple value' ++ ++ def test_kv_20_single_quoted_words_with_multi_blanks(self): ++ props = self._get_props('KEY=\' a simple value \'\n') ++ assert props.get('key', None) == ' a simple value ' ++ ++ def test_kv_21_single_quoted_word_with_double_quote(self): ++ props = self._get_props('KEY=\'a "bold" move\'\n') ++ assert props.get('key', None) == 'a "bold" move' ++ ++ def test_kv_22_quoted_unicode_wordchar(self): ++ # "wordchar" means it is in the shlex.wordchars variable. ++ props = self._get_props(u'KEY="wordchar: \u00CA (E accent grave)"\n') ++ assert props.get('key', None) == u'wordchar: \u00CA (E accent grave)' ++ ++ def test_kv_23_quoted_unicode_non_wordchar(self): ++ # "non-wordchar" means it is not in the shlex.wordchars variable. ++ props = self._get_props( ++ u'KEY="non-wordchar: \u00A1 (inverted exclamation mark)"\n') ++ assert (props.get('key', None) == ++ u'non-wordchar: \u00A1 (inverted exclamation mark)') ++ ++ def test_kv_24_double_quoted_entire_single_quoted_word(self): ++ props = self._get_props('KEY="\'value\'"\n') ++ assert props.get('key', None) == "'value'" ++ ++ def test_kv_25_single_quoted_entire_double_quoted_word(self): ++ props = self._get_props('KEY=\'"value"\'\n') ++ assert props.get('key', None) == '"value"' ++ ++ def test_kv_26_double_quoted_multiline(self): ++ props = self.distro._parse_os_release_content(StringIO( ++ 'KEY="a multi\n' ++ 'line value"\n' ++ )) ++ assert props.get('key', None) == 'a multi\nline value' ++ # TODO: Find out why the result is not 'a multi line value' ++ ++ def test_kv_27_double_quoted_multiline_2(self): ++ props = self._get_props('KEY=\' a simple value \'\n') ++ props = self.distro._parse_os_release_content(StringIO( ++ 'KEY="a multi\n' ++ 'line=value"\n' ++ )) ++ assert props.get('key', None) == 'a multi\nline=value' ++ # TODO: Find out why the result is not 'a multi line=value' ++ ++ def test_kv_28_double_quoted_word_with_equal(self): ++ props = self._get_props('KEY="var=value"\n') ++ assert props.get('key', None) == 'var=value' ++ ++ def test_kv_29_single_quoted_word_with_equal(self): ++ props = self._get_props('KEY=\'var=value\'\n') ++ assert props.get('key', None) == 'var=value' ++ ++ def test_kx_01(self): ++ props = self.distro._parse_os_release_content(StringIO( ++ 'KEY1=value1\n' ++ 'KEY2="value 2"\n' ++ )) ++ assert props.get('key1', None) == 'value1' ++ assert props.get('key2', None) == 'value 2' ++ ++ def test_kx_02(self): ++ props = self.distro._parse_os_release_content(StringIO( ++ '# KEY1=value1\n' ++ 'KEY2="value 2"\n' ++ )) ++ assert props.get('key1', None) is None ++ assert props.get('key2', None) == 'value 2' ++ ++ ++@pytest.mark.skipif(not IS_LINUX, reason='Irrelevant on non-linux') ++class TestGlobal: ++ """Test the global module-level functions, and default values of their ++ arguments. ++ """ ++ ++ def setup_method(self, test_method): ++ pass ++ ++ def test_global(self): ++ # Because the module-level functions use the module-global ++ # LinuxDistribution instance, it would influence the tested ++ # code too much if we mocked that in order to use the distro ++ # specific release files. Instead, we let the functions use ++ # the release files of the distro this test runs on, and ++ # compare the result of the global functions with the result ++ # of the methods on the global LinuxDistribution object. ++ ++ def _test_consistency(function, kwargs=None): ++ kwargs = kwargs or {} ++ method_result = getattr(MODULE_DISTRO, function)(**kwargs) ++ function_result = getattr(distro, function)(**kwargs) ++ assert method_result == function_result ++ ++ kwargs = {'full_distribution_name': True} ++ _test_consistency('linux_distribution', kwargs) ++ kwargs = {'full_distribution_name': False} ++ _test_consistency('linux_distribution', kwargs) ++ ++ kwargs = {'pretty': False} ++ _test_consistency('name', kwargs) ++ _test_consistency('version', kwargs) ++ _test_consistency('info', kwargs) ++ ++ kwargs = {'pretty': True} ++ _test_consistency('name', kwargs) ++ _test_consistency('version', kwargs) ++ _test_consistency('info', kwargs) ++ ++ kwargs = {'best': False} ++ _test_consistency('version', kwargs) ++ _test_consistency('version_parts', kwargs) ++ _test_consistency('major_version', kwargs) ++ _test_consistency('minor_version', kwargs) ++ _test_consistency('build_number', kwargs) ++ _test_consistency('info', kwargs) ++ ++ kwargs = {'best': True} ++ _test_consistency('version', kwargs) ++ _test_consistency('version_parts', kwargs) ++ _test_consistency('major_version', kwargs) ++ _test_consistency('minor_version', kwargs) ++ _test_consistency('build_number', kwargs) ++ _test_consistency('info', kwargs) ++ ++ _test_consistency('id') ++ _test_consistency('like') ++ _test_consistency('codename') ++ _test_consistency('info') ++ ++ _test_consistency('os_release_info') ++ _test_consistency('lsb_release_info') ++ _test_consistency('distro_release_info') ++ _test_consistency('uname_info') ++ ++ os_release_keys = [ ++ 'name', ++ 'version', ++ 'id', ++ 'id_like', ++ 'pretty_name', ++ 'version_id', ++ 'codename', ++ ] ++ for key in os_release_keys: ++ _test_consistency('os_release_attr', {'attribute': key}) ++ ++ lsb_release_keys = [ ++ 'distributor_id', ++ 'description', ++ 'release', ++ 'codename', ++ ] ++ for key in lsb_release_keys: ++ _test_consistency('lsb_release_attr', {'attribute': key}) ++ ++ distro_release_keys = [ ++ 'id', ++ 'name', ++ 'version_id', ++ 'codename', ++ ] ++ for key in distro_release_keys: ++ _test_consistency('distro_release_attr', {'attribute': key}) ++ ++ uname_keys = [ ++ 'id', ++ 'name', ++ 'release' ++ ] ++ for key in uname_keys: ++ _test_consistency('uname_attr', {'attribute': key}) ++ ++ ++@pytest.mark.skipif(not IS_LINUX, reason='Irrelevant on non-linux') ++class TestRepr: ++ """Test the __repr__() method. ++ """ ++ ++ def test_repr(self): ++ # We test that the class name and the names of all instance attributes ++ # show up in the repr() string. ++ repr_str = repr(distro._distro) ++ assert "LinuxDistribution" in repr_str ++ for attr in MODULE_DISTRO.__dict__.keys(): ++ assert attr + '=' in repr_str +diff --git a/third_party/python/enum34/enum/doc/enum.pdf b/third_party/python/enum34/enum/doc/enum.pdf +--- a/third_party/python/enum34/enum/doc/enum.pdf ++++ b/third_party/python/enum34/enum/doc/enum.pdf +@@ -1,10 +1,10 @@ + %PDF-1.4 +-%東京 ReportLab Generated PDF document http://www.reportlab.com ++%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com + 1 0 obj + << /F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R /F5 8 0 R /F6 15 0 R >> + endobj + 2 0 obj + << /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >> + endobj + 3 0 obj + << /BaseFont /Courier-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font >> +diff --git a/third_party/python/psutil/psutil/_pslinux.py b/third_party/python/psutil/psutil/_pslinux.py +--- a/third_party/python/psutil/psutil/_pslinux.py ++++ b/third_party/python/psutil/psutil/_pslinux.py +@@ -1046,30 +1046,28 @@ def disk_io_counters(): + # On Linux 2.4 each line has always 15 fields, e.g.: + # "3 0 8 hda 8 8 8 8 8 8 8 8 8 8 8" + # On Linux 2.6+ each line *usually* has 14 fields, and the disk + # name is in another position, like this: + # "3 0 hda 8 8 8 8 8 8 8 8 8 8 8" + # ...unless (Linux 2.6) the line refers to a partition instead + # of a disk, in which case the line has less fields (7): + # "3 1 hda1 8 8 8 8" +- # 4.18+ has 4 fields added: +- # "3 0 hda 8 8 8 8 8 8 8 8 8 8 8 0 0 0 0" + # See: + # https://www.kernel.org/doc/Documentation/iostats.txt + # https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats + fields = line.split() + fields_len = len(fields) + if fields_len == 15: + # Linux 2.4 + name = fields[3] + reads = int(fields[2]) + (reads_merged, rbytes, rtime, writes, writes_merged, + wbytes, wtime, _, busy_time, _) = map(int, fields[4:14]) +- elif fields_len == 14 or fields_len == 18: ++ elif fields_len == 14: + # Linux 2.6+, line referring to a disk + name = fields[2] + (reads, reads_merged, rbytes, rtime, writes, writes_merged, + wbytes, wtime, _, busy_time, _) = map(int, fields[3:14]) + elif fields_len == 7: + # Linux 2.6+, line referring to a partition + name = fields[2] + reads, rbytes, writes, wbytes = map(int, fields[3:]) +diff --git a/third_party/python/requirements.in b/third_party/python/requirements.in +--- a/third_party/python/requirements.in ++++ b/third_party/python/requirements.in +@@ -1,11 +1,12 @@ + attrs==18.1.0 + biplist==1.0.3 + blessings==1.7 ++distro==1.4.0 + jsmin==2.1.0 + json-e==2.7.0 + mozilla-version==0.3.0 + pathlib2==2.3.2 + pip-tools==3.0.0 + pipenv==2018.5.18 + psutil==5.4.3 + pytest==3.6.2 +diff --git a/third_party/python/requirements.txt b/third_party/python/requirements.txt +--- a/third_party/python/requirements.txt ++++ b/third_party/python/requirements.txt +@@ -14,16 +14,19 @@ blessings==1.7 \ + certifi==2018.4.16 \ + --hash=sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7 \ + --hash=sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0 \ + # via pipenv + click==7.0 \ + --hash=sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13 \ + --hash=sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7 \ + # via pip-tools ++distro==1.4.0 \ ++ --hash=sha256:362dde65d846d23baee4b5c058c8586f219b5a54be1cf5fc6ff55c4578392f57 \ ++ --hash=sha256:eedf82a470ebe7d010f1872c17237c79ab04097948800029994fa458e52fb4b4 + enum34==1.1.6 \ + --hash=sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850 \ + --hash=sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a \ + --hash=sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79 \ + --hash=sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1 \ + # via mozilla-version + funcsigs==1.0.2 \ + --hash=sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca \ + -- cgit v1.2.3