summaryrefslogtreecommitdiffstats
path: root/third_party/python/distro
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/python/distro')
-rw-r--r--third_party/python/distro/CHANGELOG.md147
-rw-r--r--third_party/python/distro/CONTRIBUTING.md54
-rw-r--r--third_party/python/distro/CONTRIBUTORS.md13
-rw-r--r--third_party/python/distro/LICENSE202
-rw-r--r--third_party/python/distro/MANIFEST.in12
-rw-r--r--third_party/python/distro/Makefile145
-rw-r--r--third_party/python/distro/PKG-INFO168
-rw-r--r--third_party/python/distro/README.md140
-rw-r--r--third_party/python/distro/dev-requirements.txt3
-rwxr-xr-xthird_party/python/distro/distro.py1216
-rwxr-xr-xthird_party/python/distro/query_local_distro.py45
-rw-r--r--third_party/python/distro/setup.cfg10
-rw-r--r--third_party/python/distro/setup.py67
13 files changed, 2222 insertions, 0 deletions
diff --git a/third_party/python/distro/CHANGELOG.md b/third_party/python/distro/CHANGELOG.md
new file mode 100644
index 0000000000..fd32d61f4f
--- /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
index 0000000000..4948ef24e9
--- /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
index 0000000000..922f6533e2
--- /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
index 0000000000..e06d208186
--- /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
index 0000000000..4d95f6ce8e
--- /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
index 0000000000..97eaba7251
--- /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 <target>" where <target> 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
index 0000000000..eacc868d57
--- /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
index 0000000000..57d681c5c1
--- /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
index 0000000000..738958e01f
--- /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
index 0000000000..33061633ef
--- /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 <https://bugs.python.org/issue1322>`_ 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 "<name>" 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 "<name>" field of the distro release file, appended
+ with the value of the pretty version ("<version_id>" and "<codename>"
+ 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 "<version_id>" 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
+ <http://www.freedesktop.org/software/systemd/man/os-release.html>`_.
+ """
+ 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 "<codename>" 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/query_local_distro.py b/third_party/python/distro/query_local_distro.py
new file mode 100755
index 0000000000..5c5ed9ef6f
--- /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
index 0000000000..51b5f83b3d
--- /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
index 0000000000..0657449267
--- /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',
+ ]
+)