From 558d75795eaf8c82fb786789fae4991d91e96538 Mon Sep 17 00:00:00 2001 From: Woomeeme <2968606874@qq.com> Date: Sun, 1 Dec 2024 18:06:14 +0800 Subject: [PATCH] upgrade python-distro (1.8.0-1) stable --- .coveragerc | 16 + .gitignore | 60 ++ CHANGELOG.md | 43 +- CONTRIBUTING.md | 4 +- Makefile | 8 +- PKG-INFO | 169 ----- README.md | 31 +- debian/changelog | 14 + debian/control | 2 + debian/rules | 2 +- distro.egg-info/PKG-INFO | 169 ----- distro.egg-info/SOURCES.txt | 153 ----- distro.egg-info/dependency_links.txt | 1 - distro.egg-info/entry_points.txt | 3 - distro.egg-info/top_level.txt | 1 - docs/conf.py | 4 +- docs/index.rst | 12 +- pyproject.toml | 3 + query_local_distro.py | 28 +- setup.cfg | 62 +- src/distro/__init__.py | 54 ++ src/distro/__main__.py | 4 + distro.py => src/distro/distro.py | 509 +++++++------- src/distro/py.typed | 0 tests/resources/distros/aix72/bin/oslevel | 3 + tests/resources/distros/aix72/bin/uname | 3 + tests/resources/distros/arch/etc/os-release | 8 +- .../distros/buildroot/etc/os-release | 5 + .../distros/centos5/etc/redhat-release | 2 +- .../distros/centos5/etc/system-release | 2 +- .../distros/centos7/etc/redhat-release | 2 +- .../distros/centos7/etc/system-release | 2 +- .../distros/debian10/bin/lsb_release | 21 + .../distros/debian10/etc/debian_version | 1 + .../resources/distros/debian10/etc/os-release | 9 + .../distros/fedora19/etc/redhat-release | 2 +- .../distros/fedora19/etc/system-release | 2 +- .../resources/distros/fedora23/etc/os-release | 15 +- .../distros/fedora23/etc/redhat-release | 2 +- .../distros/fedora23/etc/system-release | 2 +- .../resources/distros/fedora30/etc/os-release | 20 +- .../distros/fedora30/etc/redhat-release | 2 +- .../distros/fedora30/etc/system-release | 2 +- tests/resources/distros/guix/etc/os-release | 8 + .../distros/kvmibm1/etc/redhat-release | 2 +- .../distros/kvmibm1/etc/system-release | 2 +- .../distros/linuxmint17/bin/lsb_release | 44 +- .../distros/mageia5/etc/mandrake-release | 2 +- .../distros/mageia5/etc/mandrakelinux-release | 2 +- .../distros/mageia5/etc/mandriva-release | 2 +- .../resources/distros/mageia5/etc/os-release | 11 +- .../distros/mageia5/etc/redhat-release | 2 +- tests/resources/distros/mageia5/etc/release | 2 +- .../distros/mandriva2011/etc/mandrake-release | 2 +- .../mandriva2011/etc/mandrakelinux-release | 2 +- .../distros/mandriva2011/etc/redhat-release | 2 +- .../distros/mandriva2011/etc/release | 2 +- .../distros/manjaro1512/bin/lsb_release | 44 +- .../distros/opensuse15/etc/os-release | 10 + .../distros/raspbian7/etc/os-release.orig | 9 - .../distros/rhel6/etc/system-release | 2 +- .../distros/rhel7/etc/system-release | 2 +- .../distros/rocky/etc/centos-release | 1 + tests/resources/distros/rocky/etc/os-release | 1 + .../distros/rocky/etc/redhat-release | 1 + .../resources/distros/rocky/etc/rocky-release | 1 + .../distros/rocky/etc/rocky-release-upstream | 1 + .../distros/rocky/etc/system-release | 1 + .../distros/rocky/etc/system-release-cpe | 1 + .../distros/rocky/usr/lib/os-release | 13 + .../distro/dontincludeuname/bin/uname | 3 + .../testdistros/distro/emptyuname/bin/uname | 3 + tests/test_distro.py | 632 +++++++++++------- setup.py => tox.ini | 19 +- 74 files changed, 1045 insertions(+), 1241 deletions(-) create mode 100644 .coveragerc create mode 100644 .gitignore delete mode 100644 PKG-INFO delete mode 100644 distro.egg-info/PKG-INFO delete mode 100644 distro.egg-info/SOURCES.txt delete mode 100644 distro.egg-info/dependency_links.txt delete mode 100644 distro.egg-info/entry_points.txt delete mode 100644 distro.egg-info/top_level.txt create mode 100644 pyproject.toml create mode 100644 src/distro/__init__.py create mode 100644 src/distro/__main__.py rename distro.py => src/distro/distro.py (79%) create mode 100644 src/distro/py.typed create mode 100755 tests/resources/distros/aix72/bin/oslevel create mode 100755 tests/resources/distros/aix72/bin/uname mode change 100644 => 120000 tests/resources/distros/arch/etc/os-release create mode 100644 tests/resources/distros/buildroot/etc/os-release mode change 100644 => 120000 tests/resources/distros/centos5/etc/redhat-release mode change 100644 => 120000 tests/resources/distros/centos5/etc/system-release mode change 100644 => 120000 tests/resources/distros/centos7/etc/redhat-release mode change 100644 => 120000 tests/resources/distros/centos7/etc/system-release create mode 100644 tests/resources/distros/debian10/bin/lsb_release create mode 100644 tests/resources/distros/debian10/etc/debian_version create mode 100644 tests/resources/distros/debian10/etc/os-release mode change 100644 => 120000 tests/resources/distros/fedora19/etc/redhat-release mode change 100644 => 120000 tests/resources/distros/fedora19/etc/system-release mode change 100644 => 120000 tests/resources/distros/fedora23/etc/os-release mode change 100644 => 120000 tests/resources/distros/fedora23/etc/redhat-release mode change 100644 => 120000 tests/resources/distros/fedora23/etc/system-release mode change 100644 => 120000 tests/resources/distros/fedora30/etc/os-release mode change 100644 => 120000 tests/resources/distros/fedora30/etc/redhat-release mode change 100644 => 120000 tests/resources/distros/fedora30/etc/system-release create mode 100644 tests/resources/distros/guix/etc/os-release mode change 100644 => 120000 tests/resources/distros/kvmibm1/etc/redhat-release mode change 100644 => 120000 tests/resources/distros/kvmibm1/etc/system-release mode change 100755 => 120000 tests/resources/distros/linuxmint17/bin/lsb_release mode change 100644 => 120000 tests/resources/distros/mageia5/etc/mandrake-release mode change 100644 => 120000 tests/resources/distros/mageia5/etc/mandrakelinux-release mode change 100644 => 120000 tests/resources/distros/mageia5/etc/mandriva-release mode change 100644 => 120000 tests/resources/distros/mageia5/etc/os-release mode change 100644 => 120000 tests/resources/distros/mageia5/etc/redhat-release mode change 100644 => 120000 tests/resources/distros/mageia5/etc/release mode change 100644 => 120000 tests/resources/distros/mandriva2011/etc/mandrake-release mode change 100644 => 120000 tests/resources/distros/mandriva2011/etc/mandrakelinux-release mode change 100644 => 120000 tests/resources/distros/mandriva2011/etc/redhat-release mode change 100644 => 120000 tests/resources/distros/mandriva2011/etc/release mode change 100755 => 120000 tests/resources/distros/manjaro1512/bin/lsb_release create mode 100644 tests/resources/distros/opensuse15/etc/os-release delete mode 100644 tests/resources/distros/raspbian7/etc/os-release.orig mode change 100644 => 120000 tests/resources/distros/rhel6/etc/system-release mode change 100644 => 120000 tests/resources/distros/rhel7/etc/system-release create mode 120000 tests/resources/distros/rocky/etc/centos-release create mode 120000 tests/resources/distros/rocky/etc/os-release create mode 120000 tests/resources/distros/rocky/etc/redhat-release create mode 100644 tests/resources/distros/rocky/etc/rocky-release create mode 100644 tests/resources/distros/rocky/etc/rocky-release-upstream create mode 120000 tests/resources/distros/rocky/etc/system-release create mode 100644 tests/resources/distros/rocky/etc/system-release-cpe create mode 100644 tests/resources/distros/rocky/usr/lib/os-release create mode 100644 tests/resources/testdistros/distro/dontincludeuname/bin/uname create mode 100644 tests/resources/testdistros/distro/emptyuname/bin/uname rename setup.py => tox.ini (59%) diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..d049606 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,16 @@ +[run] +branch = True +source = + distro.py + +[paths] +source = + distro.py + .tox/*/lib/python*/site-packages/distro.py + .tox/pypy/site-packages/distro.py + +[report] +exclude_lines = + if __name__ == .__main__.: + raise ImportError + raise subprocess.CalledProcessError \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e9b50a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,60 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +/env/ +/bin/ +/build/ +/develop-eggs/ +/dist/ +/eggs/ +/lib/ +/lib64/ +/parts/ +/sdist/ +/var/ +/*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +/htmlcov/ +/.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Django stuff: +*.log +*.pot + +# Sphinx documentation +/docs/_build/ + +*.iml + +*COMMIT_MSG + +# QuickBuild +.qbcache/ diff --git a/CHANGELOG.md b/CHANGELOG.md index c2da7ec..75588d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,43 @@ +## 1.8.0 (2022.10.10) + +BACKWARD COMPATIBILITY: +* Replace `setup.py` with `build` [[#342](https://github.com/python-distro/distro/pull/342)] + +ENHANCEMENTS: +* Lowered `LinuxDistribution._distro_release_info` method complexity [[#327](https://github.com/python-distro/distro/pull/327)] +* Added official support for Buildroot distribution [[#329](https://github.com/python-distro/distro/pull/329)] +* Added official support for Guix distribution [[#330](https://github.com/python-distro/distro/pull/330)] +* Added support for `/etc/debian_version` [[#333](https://github.com/python-distro/distro/pull/333)] & [[#349](https://github.com/python-distro/distro/pull/349)] +* Fixed a typography in CONTRIBUTING.md [[#340](https://github.com/python-distro/distro/pull/340)] +* Improved README.md "Usage" code block [[#343](https://github.com/python-distro/distro/pull/343)] + +RELEASE: +* Bumped black to v22.3.0 in pre-commit.ci configuration [[#331](https://github.com/python-distro/distro/pull/331)] +* Enabled GitHub Dependabot to keep GitHub Actions up to date [[#335](https://github.com/python-distro/distro/pull/335)] + +## 1.7.0 (2022.02.15) + +BACKWARD COMPATIBILITY: +* Dropped support for EOL Pythons 2.7, 3.4 and 3.5 [[#281](https://github.com/python-distro/distro/pull/281)] +* Dropped support for LSB and `uname` back-ends when `--root-dir` is specified [[#311](https://github.com/python-distro/distro/pull/311)] +* Moved `distro.py` to `src/distro/distro.py` [[#315](https://github.com/python-distro/distro/pull/315)] + +ENHANCEMENTS: +* Documented that `distro.version()` can return an empty string on rolling releases [[#312](https://github.com/python-distro/distro/pull/312)] +* Documented support for Python 3.10 [[#316](https://github.com/python-distro/distro/pull/316)] +* Added official support for Rocky Linux distribution [[#318](https://github.com/python-distro/distro/pull/318)] +* Added a shebang to `distro.py` to allow standalone execution [[#313](https://github.com/python-distro/distro/pull/313)] +* Added support for AIX platforms [[#311](https://github.com/python-distro/distro/pull/311)] +* Added compliance for PEP-561 [[#315](https://github.com/python-distro/distro/pull/315)] + +BUG FIXES: +* Fixed `include_uname` parameter oversight [[#305](https://github.com/python-distro/distro/pull/305)] +* Fixed crash when `uname -rs` output is empty [[#304](https://github.com/python-distro/distro/pull/304)] +* Fixed Amazon Linux identifier in `distro.id()` documentation [[#318](https://github.com/python-distro/distro/pull/318)] +* Fixed OpenSuse >= 15 support [[#319](https://github.com/python-distro/distro/pull/319)] +* Fixed encoding issues when opening distro release files [[#324](https://github.com/python-distro/distro/pull/324)] +* Fixed `linux_distribution` regression introduced in [[#230](https://github.com/python-distro/distro/pull/230)] [[#325](https://github.com/python-distro/distro/pull/325)] + ## 1.6.0 (2021.7.30) BACKWARDS COMPATIBILITY: @@ -107,7 +147,7 @@ RELEASE: ## 1.0.4 (2017.04.01) BUG FIXES: -* Guess common *-release files if /etc not readable [[#175](https://github.com/python-distro/distro/issues/175)] +* Guess common \*-release files if /etc not readable [[#175](https://github.com/python-distro/distro/issues/175)] ## 1.0.3 (2017.03.19) @@ -190,4 +230,3 @@ TESTS: DOCS: * Documentation fixes - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4948ef2..262469a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,6 +49,6 @@ def test_centos5_dist_release(self): 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`. +Where the name of the method is not indicative of the lookup folder but rather the 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 +A test case is mandatory under `TestOverall` for a PR to be complete. diff --git a/Makefile b/Makefile index f3e6a35..ad3ec56 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ PACKAGENAME = distro # You can set these variables from the command line, and also # from the environment for the first two. -SPHINXOPTS ?= -v +SPHINXOPTS ?= -n -v SPHINXBUILD ?= sphinx-build SPHINXSOURCEDIR = docs SPHINXBUILDDIR = docs/_build @@ -54,7 +54,7 @@ clean: .PHONY: build build: - python setup.py sdist bdist_wheel + python -m build .PHONY: publish publish: @@ -72,12 +72,12 @@ dev: instdev test .PHONY: instdev instdev: pip install -r dev-requirements.txt - python setup.py develop + pip install -e . @echo "$@ done." .PHONY: install install: - python setup.py install + pip install . @echo "$@ done." .PHONY: clobber diff --git a/PKG-INFO b/PKG-INFO deleted file mode 100644 index bc42dfc..0000000 --- a/PKG-INFO +++ /dev/null @@ -1,169 +0,0 @@ -Metadata-Version: 2.1 -Name: distro -Version: 1.6.0 -Summary: Distro - an OS platform information API -Home-page: https://github.com/python-distro/distro -Author: Nir Cohen -Author-email: nir36g@gmail.com -License: Apache License, Version 2.0 -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: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: System :: Operating System -Description-Content-Type: text/markdown -License-File: LICENSE - -Distro - an OS platform information API -======================================= - -[![CI Status](https://github.com/python-distro/distro/workflows/CI/badge.svg)](https://github.com/python-distro/distro/actions/workflows/ci.yaml) -[![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) -[![Code Coverage](https://codecov.io/github/python-distro/distro/coverage.svg?branch=master)](https://codecov.io/github/python-distro/distro?branch=master) -[![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/python-distro/distro](https://badges.gitter.im/python-distro/distro.svg)](https://gitter.im/python-distro/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 (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/python-distro/distro/issues/177) is also planned. - -For Python 2.6 support, see https://github.com/python-distro/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/python-distro/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 removed it altogether. Its predecessor function -[`platform.dist`](https://docs.python.org/3.7/library/platform.html#platform.dist) -was already deprecated since Python 2.6 and 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, with a fall-back on `/usr/lib/os-release` if needed. -* 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/python-distro/distro/tree/master/tests/resources/distros). - - -## Testing - -```shell -git clone git@github.com:python-distro/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/python-distro/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/README.md b/README.md index 76eaef2..de6f3c7 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,20 @@ Installation of the latest development version: pip install https://github.com/python-distro/distro/archive/master.tar.gz ``` +To use as a standalone script, download `distro.py` directly: + +```shell +curl -O https://raw.githubusercontent.com/python-distro/distro/master/src/distro/distro.py +python distro.py +``` + +``distro`` is safe to vendor within projects that do not wish to add +dependencies. + +```shell +cd myproject +curl -O https://raw.githubusercontent.com/python-distro/distro/master/src/distro/distro.py +``` ## Usage @@ -61,8 +75,12 @@ $ distro -j $ python >>> import distro ->>> distro.linux_distribution(full_distribution_name=False) -('centos', '7.1.1503', 'Core') +>>> distro.name(pretty=True) +'CentOS Linux 8' +>>> distro.id() +'centos' +>>> distro.version(best=True) +'8.4.2105' ``` @@ -92,9 +110,8 @@ namely from these data sources (from high to low precedence): ## 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. +`distro` is supported and tested on Python 3.6+ 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/python-distro/distro/tree/master/tests/resources/distros). @@ -129,7 +146,7 @@ formats can be found here: * 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.debian.org/stable/python3-distro * https://packages.gentoo.org/packages/dev-python/distro -* https://pkgs.org/download/python2-distro +* https://pkgs.org/download/python3-distro * https://slackbuilds.org/repository/14.2/python/python-distro/ diff --git a/debian/changelog b/debian/changelog index 0aa35a4..2805eff 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,17 @@ +python-distro (1.8.0-1) unstable; urgency=medium + + * New upstream release. + * Build with pybuild. + + -- Stefano Rivera Fri, 14 Oct 2022 17:00:22 +0200 + +python-distro (1.7.0-1) unstable; urgency=medium + + * New upstream version + * Add salsa-ci file (routine-update) + + -- Stefano Rivera Sat, 19 Feb 2022 10:54:14 -0400 + python-distro (1.6.0-2) unstable; urgency=medium [ Stefano Rivera ] diff --git a/debian/control b/debian/control index dd69e15..1e929ed 100644 --- a/debian/control +++ b/debian/control @@ -6,6 +6,8 @@ Uploaders: Stefano Rivera Build-Depends: debhelper-compat (= 13), dh-python, + dh-sequence-python3, + pybuild-plugin-pyproject, python3-all, python3-setuptools Build-Depends-Indep: python3-pytest diff --git a/debian/rules b/debian/rules index d759c48..8dcf5bb 100755 --- a/debian/rules +++ b/debian/rules @@ -5,7 +5,7 @@ export PYBUILD_NAME=distro #export DH_VERBOSE=1 %: - dh $@ --with python3 --buildsystem=pybuild + dh $@ --buildsystem pybuild # For now, I do not want to distribute /usr/bin/distro diff --git a/distro.egg-info/PKG-INFO b/distro.egg-info/PKG-INFO deleted file mode 100644 index bc42dfc..0000000 --- a/distro.egg-info/PKG-INFO +++ /dev/null @@ -1,169 +0,0 @@ -Metadata-Version: 2.1 -Name: distro -Version: 1.6.0 -Summary: Distro - an OS platform information API -Home-page: https://github.com/python-distro/distro -Author: Nir Cohen -Author-email: nir36g@gmail.com -License: Apache License, Version 2.0 -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: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: System :: Operating System -Description-Content-Type: text/markdown -License-File: LICENSE - -Distro - an OS platform information API -======================================= - -[![CI Status](https://github.com/python-distro/distro/workflows/CI/badge.svg)](https://github.com/python-distro/distro/actions/workflows/ci.yaml) -[![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) -[![Code Coverage](https://codecov.io/github/python-distro/distro/coverage.svg?branch=master)](https://codecov.io/github/python-distro/distro?branch=master) -[![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/python-distro/distro](https://badges.gitter.im/python-distro/distro.svg)](https://gitter.im/python-distro/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 (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/python-distro/distro/issues/177) is also planned. - -For Python 2.6 support, see https://github.com/python-distro/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/python-distro/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 removed it altogether. Its predecessor function -[`platform.dist`](https://docs.python.org/3.7/library/platform.html#platform.dist) -was already deprecated since Python 2.6 and 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, with a fall-back on `/usr/lib/os-release` if needed. -* 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/python-distro/distro/tree/master/tests/resources/distros). - - -## Testing - -```shell -git clone git@github.com:python-distro/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/python-distro/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/distro.egg-info/SOURCES.txt b/distro.egg-info/SOURCES.txt deleted file mode 100644 index 1e7300e..0000000 --- a/distro.egg-info/SOURCES.txt +++ /dev/null @@ -1,153 +0,0 @@ -CHANGELOG.md -CONTRIBUTING.md -CONTRIBUTORS.md -LICENSE -MANIFEST.in -Makefile -README.md -dev-requirements.txt -distro.py -query_local_distro.py -setup.cfg -setup.py -distro.egg-info/PKG-INFO -distro.egg-info/SOURCES.txt -distro.egg-info/dependency_links.txt -distro.egg-info/entry_points.txt -distro.egg-info/top_level.txt -docs/conf.py -docs/index.rst -tests/__init__.py -tests/test_distro.py -tests/resources/cli/fedora30/etc/fedora-release -tests/resources/cli/fedora30/etc/os-release -tests/resources/cli/fedora30/etc/redhat-release -tests/resources/cli/fedora30/etc/system-release -tests/resources/distros/__shared__/bin/lsb_release -tests/resources/distros/amazon2014/etc/system-release -tests/resources/distros/amazon2016/etc/os-release -tests/resources/distros/amazon2016/etc/system-release -tests/resources/distros/arch/etc/arch-release -tests/resources/distros/arch/etc/os-release -tests/resources/distros/arch/usr/lib/os-release -tests/resources/distros/centos5/etc/centos-release -tests/resources/distros/centos5/etc/redhat-release -tests/resources/distros/centos5/etc/system-release -tests/resources/distros/centos7/etc/centos-release -tests/resources/distros/centos7/etc/os-release -tests/resources/distros/centos7/etc/redhat-release -tests/resources/distros/centos7/etc/system-release -tests/resources/distros/cloudlinux5/etc/redhat-release -tests/resources/distros/cloudlinux6/etc/redhat-release -tests/resources/distros/cloudlinux7/etc/os-release -tests/resources/distros/cloudlinux7/etc/redhat-release -tests/resources/distros/coreos/etc/oem-release -tests/resources/distros/coreos/etc/os-release -tests/resources/distros/debian8/bin/lsb_release -tests/resources/distros/debian8/etc/debian_version -tests/resources/distros/debian8/etc/os-release -tests/resources/distros/exherbo/etc/os-release -tests/resources/distros/fedora19/etc/fedora-release -tests/resources/distros/fedora19/etc/issue -tests/resources/distros/fedora19/etc/issue.net -tests/resources/distros/fedora19/etc/os-release -tests/resources/distros/fedora19/etc/redhat-release -tests/resources/distros/fedora19/etc/system-release -tests/resources/distros/fedora19/etc/system-release-cpe -tests/resources/distros/fedora23/etc/fedora-release -tests/resources/distros/fedora23/etc/os-release -tests/resources/distros/fedora23/etc/redhat-release -tests/resources/distros/fedora23/etc/system-release -tests/resources/distros/fedora23/usr/lib/os-release -tests/resources/distros/fedora30/etc/fedora-release -tests/resources/distros/fedora30/etc/os-release -tests/resources/distros/fedora30/etc/redhat-release -tests/resources/distros/fedora30/etc/system-release -tests/resources/distros/fedora30/usr/lib/os-release -tests/resources/distros/freebsd111/bin/uname -tests/resources/distros/gentoo/etc/gentoo-release -tests/resources/distros/gentoo/etc/os-release -tests/resources/distros/kali/etc/os-release -tests/resources/distros/kvmibm1/bin/lsb_release -tests/resources/distros/kvmibm1/etc/base-release -tests/resources/distros/kvmibm1/etc/os-release -tests/resources/distros/kvmibm1/etc/redhat-release -tests/resources/distros/kvmibm1/etc/system-release -tests/resources/distros/linuxmint17/bin/lsb_release -tests/resources/distros/linuxmint17/etc/debian_version -tests/resources/distros/linuxmint17/etc/lsb-release -tests/resources/distros/linuxmint17/etc/os-release -tests/resources/distros/linuxmint17/etc/upstream-release/lsb-release -tests/resources/distros/mageia5/bin/lsb_release -tests/resources/distros/mageia5/etc/lsb-release -tests/resources/distros/mageia5/etc/mageia-release -tests/resources/distros/mageia5/etc/mandrake-release -tests/resources/distros/mageia5/etc/mandrakelinux-release -tests/resources/distros/mageia5/etc/mandriva-release -tests/resources/distros/mageia5/etc/os-release -tests/resources/distros/mageia5/etc/redhat-release -tests/resources/distros/mageia5/etc/release -tests/resources/distros/mageia5/etc/version -tests/resources/distros/mageia5/usr/lib/os-release -tests/resources/distros/mandriva2011/bin/lsb_release -tests/resources/distros/mandriva2011/etc/lsb-release -tests/resources/distros/mandriva2011/etc/mandrake-release -tests/resources/distros/mandriva2011/etc/mandrakelinux-release -tests/resources/distros/mandriva2011/etc/mandriva-release -tests/resources/distros/mandriva2011/etc/redhat-release -tests/resources/distros/mandriva2011/etc/release -tests/resources/distros/mandriva2011/etc/version -tests/resources/distros/manjaro1512/bin/lsb_release -tests/resources/distros/manjaro1512/etc/lsb-release -tests/resources/distros/manjaro1512/etc/manjaro-release -tests/resources/distros/manjaro1512/etc/os-release -tests/resources/distros/midnightbsd12/bin/uname -tests/resources/distros/netbsd711/bin/uname -tests/resources/distros/openbsd62/bin/uname -tests/resources/distros/openelec6/etc/os-release -tests/resources/distros/opensuse42/etc/SuSE-release -tests/resources/distros/opensuse42/etc/os-release -tests/resources/distros/oracle7/etc/oracle-release -tests/resources/distros/oracle7/etc/os-release -tests/resources/distros/raspbian7/etc/debian_version -tests/resources/distros/raspbian7/etc/os-release -tests/resources/distros/raspbian7/etc/os-release.orig -tests/resources/distros/raspbian8/etc/debian_version -tests/resources/distros/raspbian8/etc/os-release -tests/resources/distros/rhel5/etc/redhat-release -tests/resources/distros/rhel6/etc/redhat-release -tests/resources/distros/rhel6/etc/system-release -tests/resources/distros/rhel7/etc/os-release -tests/resources/distros/rhel7/etc/redhat-release -tests/resources/distros/rhel7/etc/system-release -tests/resources/distros/scientific6/etc/redhat-release -tests/resources/distros/scientific6/etc/system-release -tests/resources/distros/scientific7/etc/os-release -tests/resources/distros/scientific7/etc/redhat-release -tests/resources/distros/scientific7/etc/sl-release -tests/resources/distros/scientific7/etc/system-release -tests/resources/distros/slackware14/etc/os-release -tests/resources/distros/slackware14/etc/slackware-version -tests/resources/distros/sles12/bin/lsb_release -tests/resources/distros/sles12/etc/SuSE-release -tests/resources/distros/sles12/etc/os-release -tests/resources/distros/ubuntu14/bin/lsb_release -tests/resources/distros/ubuntu14/etc/debian_version -tests/resources/distros/ubuntu14/etc/lsb-release -tests/resources/distros/ubuntu14/etc/os-release -tests/resources/distros/ubuntu16/bin/lsb_release -tests/resources/distros/ubuntu16/etc/debian_version -tests/resources/distros/ubuntu16/etc/lsb-release -tests/resources/distros/ubuntu16/etc/os-release -tests/resources/special/empty-release -tests/resources/testdistros/distro/baduname/bin/uname -tests/resources/testdistros/distro/unknowndistro/etc/unknowndistro-release -tests/resources/testdistros/distro/usrlibosreleaseonly/usr/lib/os-release -tests/resources/testdistros/lsb/lsb_rc001/bin/lsb_release -tests/resources/testdistros/lsb/lsb_rc002/bin/lsb_release -tests/resources/testdistros/lsb/lsb_rc126/bin/lsb_release -tests/resources/testdistros/lsb/lsb_rc130/bin/lsb_release -tests/resources/testdistros/lsb/lsb_rc255/bin/lsb_release -tests/resources/testdistros/lsb/ubuntu14_nomodules/bin/lsb_release -tests/resources/testdistros/lsb/ubuntu14_normal/bin/lsb_release -tests/resources/testdistros/lsb/ubuntu14_trailingblanks/bin/lsb_release \ No newline at end of file diff --git a/distro.egg-info/dependency_links.txt b/distro.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/distro.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/distro.egg-info/entry_points.txt b/distro.egg-info/entry_points.txt deleted file mode 100644 index dd40239..0000000 --- a/distro.egg-info/entry_points.txt +++ /dev/null @@ -1,3 +0,0 @@ -[console_scripts] -distro = distro:main - diff --git a/distro.egg-info/top_level.txt b/distro.egg-info/top_level.txt deleted file mode 100644 index 0e09331..0000000 --- a/distro.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -distro diff --git a/docs/conf.py b/docs/conf.py index 4b6dcd0..f52456e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -25,10 +25,10 @@ # The short X.Y version. # Note: We use the full version in both cases. -version = distro.__version__ +version = distro.__version__ # type: ignore # The full version, including alpha/beta/rc tags -release = distro.__version__ +release = version # -- General configuration --------------------------------------------------- diff --git a/docs/index.rst b/docs/index.rst index 38c47cb..d320e66 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,13 +19,13 @@ If you want to jump into the API description right away, read about the 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. +The ``distro`` package is supported on Python 3.6+ 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 `_. +This package is tested on Python 3.6+ 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`_ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..fed528d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" diff --git a/query_local_distro.py b/query_local_distro.py index 7a65cfc..7941c73 100755 --- a/query_local_distro.py +++ b/query_local_distro.py @@ -13,14 +13,12 @@ # 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): +def pprint(obj: object) -> None: for line in pformat(obj).split("\n"): print(4 * " " + line) @@ -31,15 +29,15 @@ def pprint(obj): 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())) +print(f"id: {distro.id()}") +print(f"name: {distro.name()}") +print(f"name_pretty: {distro.name(True)}") +print(f"version: {distro.version()}") +print(f"version_pretty: {distro.version(True)}") +print(f"like: {distro.like()}") +print(f"codename: {distro.codename()}") +print(f"linux_distribution_full: {distro.linux_distribution()}") +print(f"linux_distribution: {distro.linux_distribution(False)}") +print(f"major_version: {distro.major_version()}") +print(f"minor_version: {distro.minor_version()}") +print(f"build_number: {distro.build_number()}") diff --git a/setup.cfg b/setup.cfg index f578d04..c163a94 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,48 +6,44 @@ author = Nir Cohen author_email = nir36g@gmail.com license = Apache License, Version 2.0 platforms = All -description = Distro - an OS platform information API +description= Distro - an OS platform information API long_description = file: README.md long_description_content_type = text/markdown -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 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Topic :: Software Development :: Libraries :: Python Modules - Topic :: System :: Operating System +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 :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Topic :: Software Development :: Libraries :: Python Modules + Topic :: System :: Operating System [options] -py_modules = distro +package_dir = + = src +packages = distro +python_requires = >=3.6 -[options.entry_points] -console_scripts = - distro = distro:main +[options.package_data] +* = py.typed -[bdist_wheel] -universal = 1 +[options.entry_points] +console_scripts = + distro = distro.distro:main [flake8] max-line-length = 88 [isort] profile = black - -[egg_info] -tag_build = -tag_date = 0 - diff --git a/src/distro/__init__.py b/src/distro/__init__.py new file mode 100644 index 0000000..7686fe8 --- /dev/null +++ b/src/distro/__init__.py @@ -0,0 +1,54 @@ +from .distro import ( + NORMALIZED_DISTRO_ID, + NORMALIZED_LSB_ID, + NORMALIZED_OS_ID, + LinuxDistribution, + __version__, + build_number, + codename, + distro_release_attr, + distro_release_info, + id, + info, + like, + linux_distribution, + lsb_release_attr, + lsb_release_info, + major_version, + minor_version, + name, + os_release_attr, + os_release_info, + uname_attr, + uname_info, + version, + version_parts, +) + +__all__ = [ + "NORMALIZED_DISTRO_ID", + "NORMALIZED_LSB_ID", + "NORMALIZED_OS_ID", + "LinuxDistribution", + "build_number", + "codename", + "distro_release_attr", + "distro_release_info", + "id", + "info", + "like", + "linux_distribution", + "lsb_release_attr", + "lsb_release_info", + "major_version", + "minor_version", + "name", + "os_release_attr", + "os_release_info", + "uname_attr", + "uname_info", + "version", + "version_parts", +] + +__version__ = __version__ diff --git a/src/distro/__main__.py b/src/distro/__main__.py new file mode 100644 index 0000000..0c01d5b --- /dev/null +++ b/src/distro/__main__.py @@ -0,0 +1,4 @@ +from .distro import main + +if __name__ == "__main__": + main() diff --git a/distro.py b/src/distro/distro.py similarity index 79% rename from distro.py rename to src/distro/distro.py index 7892741..89e1868 100755 --- a/distro.py +++ b/src/distro/distro.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # Copyright 2015,2016,2017 Nir Cohen # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,40 +37,39 @@ import subprocess import sys import warnings +from typing import ( + Any, + Callable, + Dict, + Iterable, + Optional, + Sequence, + TextIO, + Tuple, + Type, +) -__version__ = "1.6.0" - -# Use `if False` to avoid an ImportError on Python 2. After dropping Python 2 -# support, can use typing.TYPE_CHECKING instead. See: -# https://docs.python.org/3/library/typing.html#typing.TYPE_CHECKING -if False: # pragma: nocover - from typing import ( - Any, - Callable, - Dict, - Iterable, - Optional, - Sequence, - TextIO, - Tuple, - Type, - TypedDict, - Union, - ) +try: + from typing import TypedDict +except ImportError: + # Python 3.7 + TypedDict = dict - VersionDict = TypedDict( - "VersionDict", {"major": str, "minor": str, "build_number": str} - ) - InfoDict = TypedDict( - "InfoDict", - { - "id": str, - "version": str, - "version_parts": VersionDict, - "like": str, - "codename": str, - }, - ) +__version__ = "1.8.0" + + +class VersionDict(TypedDict): + major: str + minor: str + build_number: str + + +class InfoDict(TypedDict): + id: str + version: str + version_parts: VersionDict + like: str + codename: str _UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc") @@ -85,6 +85,7 @@ #: * Value: Normalized value. NORMALIZED_OS_ID = { "ol": "oracle", # Oracle Linux + "opensuse-leap": "opensuse", # Newer versions of OpenSuSE report as opensuse-leap } #: Translation table for normalizing the "Distributor ID" attribute returned by @@ -121,6 +122,26 @@ # Pattern for base file name of distro release file _DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$") +# Base file names to be looked up for if _UNIXCONFDIR is not readable. +_DISTRO_RELEASE_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", + "rocky-release", + "sl-release", + "slackware-version", +] + # Base file names to be ignored when searching for distro release file _DISTRO_RELEASE_IGNORE_BASENAMES = ( "debian_version", @@ -133,8 +154,7 @@ ) -def linux_distribution(full_distribution_name=True): - # type: (bool) -> Tuple[str, str, str] +def linux_distribution(full_distribution_name: bool = True) -> Tuple[str, str, str]: """ .. deprecated:: 1.6.0 @@ -151,7 +171,8 @@ def linux_distribution(full_distribution_name=True): * ``version``: The result of :func:`distro.version`. - * ``codename``: The result of :func:`distro.codename`. + * ``codename``: The extra item (usually in parentheses) after the + os-release version number, or 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 @@ -176,8 +197,7 @@ def linux_distribution(full_distribution_name=True): return _distro.linux_distribution(full_distribution_name) -def id(): - # type: () -> str +def id() -> str: """ Return the distro ID of the current distribution, as a machine-readable string. @@ -198,8 +218,9 @@ def id(): "fedora" Fedora "sles" SUSE Linux Enterprise Server "opensuse" openSUSE - "amazon" Amazon Linux + "amzn" Amazon Linux "arch" Arch Linux + "buildroot" Buildroot "cloudlinux" CloudLinux OS "exherbo" Exherbo Linux "gentoo" GenToo Linux @@ -219,6 +240,9 @@ def id(): "netbsd" NetBSD "freebsd" FreeBSD "midnightbsd" MidnightBSD + "rocky" Rocky Linux + "aix" AIX + "guix" Guix System ============== ========================================= If you have a need to get distros for reliable IDs added into this set, @@ -256,8 +280,7 @@ def id(): return _distro.id() -def name(pretty=False): - # type: (bool) -> str +def name(pretty: bool = False) -> str: """ Return the name of the current OS distribution, as a human-readable string. @@ -296,8 +319,7 @@ def name(pretty=False): return _distro.name(pretty) -def version(pretty=False, best=False): - # type: (bool, bool) -> str +def version(pretty: bool = False, best: bool = False) -> str: """ Return the version of the current OS distribution, as a human-readable string. @@ -313,6 +335,10 @@ def version(pretty=False, best=False): sources in a fixed priority order does not always yield the most precise version (e.g. for Debian 8.2, or CentOS 7.1). + Some other distributions may not provide this kind of information. In these + cases, an empty string would be returned. This behavior can be observed + with rolling releases distributions (e.g. Arch Linux). + The *best* parameter can be used to control the approach for the returned version: @@ -341,8 +367,7 @@ def version(pretty=False, best=False): return _distro.version(pretty, best) -def version_parts(best=False): - # type: (bool) -> Tuple[str, str, str] +def version_parts(best: bool = False) -> Tuple[str, str, str]: """ Return the version of the current OS distribution as a tuple ``(major, minor, build_number)`` with items as follows: @@ -359,8 +384,7 @@ def version_parts(best=False): return _distro.version_parts(best) -def major_version(best=False): - # type: (bool) -> str +def major_version(best: bool = False) -> str: """ Return the major version of the current OS distribution, as a string, if provided. @@ -373,8 +397,7 @@ def major_version(best=False): return _distro.major_version(best) -def minor_version(best=False): - # type: (bool) -> str +def minor_version(best: bool = False) -> str: """ Return the minor version of the current OS distribution, as a string, if provided. @@ -387,8 +410,7 @@ def minor_version(best=False): return _distro.minor_version(best) -def build_number(best=False): - # type: (bool) -> str +def build_number(best: bool = False) -> str: """ Return the build number of the current OS distribution, as a string, if provided. @@ -401,8 +423,7 @@ def build_number(best=False): return _distro.build_number(best) -def like(): - # type: () -> str +def like() -> str: """ Return a space-separated list of distro IDs of distributions that are closely related to the current OS distribution in regards to packaging @@ -419,8 +440,7 @@ def like(): return _distro.like() -def codename(): - # type: () -> str +def codename() -> str: """ Return the codename for the release of the current OS distribution, as a string. @@ -444,8 +464,7 @@ def codename(): return _distro.codename() -def info(pretty=False, best=False): - # type: (bool, bool) -> InfoDict +def info(pretty: bool = False, best: bool = False) -> InfoDict: """ Return certain machine-readable information items about the current OS distribution in a dictionary, as shown in the following example: @@ -489,8 +508,7 @@ def info(pretty=False, best=False): return _distro.info(pretty, best) -def os_release_info(): - # type: () -> Dict[str, str] +def os_release_info() -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the os-release file data source of the current OS distribution. @@ -500,8 +518,7 @@ def os_release_info(): return _distro.os_release_info() -def lsb_release_info(): - # type: () -> Dict[str, str] +def lsb_release_info() -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the lsb_release command data source of the current OS distribution. @@ -512,8 +529,7 @@ def lsb_release_info(): return _distro.lsb_release_info() -def distro_release_info(): - # type: () -> Dict[str, str] +def distro_release_info() -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the distro release file data source of the current OS distribution. @@ -523,8 +539,7 @@ def distro_release_info(): return _distro.distro_release_info() -def uname_info(): - # type: () -> Dict[str, str] +def uname_info() -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the distro release file data source of the current OS distribution. @@ -532,8 +547,7 @@ def uname_info(): return _distro.uname_info() -def os_release_attr(attribute): - # type: (str) -> str +def os_release_attr(attribute: str) -> str: """ Return a single named information item from the os-release file data source of the current OS distribution. @@ -552,8 +566,7 @@ def os_release_attr(attribute): return _distro.os_release_attr(attribute) -def lsb_release_attr(attribute): - # type: (str) -> str +def lsb_release_attr(attribute: str) -> str: """ Return a single named information item from the lsb_release command output data source of the current OS distribution. @@ -573,8 +586,7 @@ def lsb_release_attr(attribute): return _distro.lsb_release_attr(attribute) -def distro_release_attr(attribute): - # type: (str) -> str +def distro_release_attr(attribute: str) -> str: """ Return a single named information item from the distro release file data source of the current OS distribution. @@ -593,8 +605,7 @@ def distro_release_attr(attribute): return _distro.distro_release_attr(attribute) -def uname_attr(attribute): - # type: (str) -> str +def uname_attr(attribute: str) -> str: """ Return a single named information item from the distro release file data source of the current OS distribution. @@ -615,25 +626,23 @@ def uname_attr(attribute): from functools import cached_property except ImportError: # Python < 3.8 - class cached_property(object): # type: ignore + class cached_property: # type: ignore """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): - # type: (Callable[[Any], Any]) -> None + def __init__(self, f: Callable[[Any], Any]) -> None: self._fname = f.__name__ self._f = f - def __get__(self, obj, owner): - # type: (Any, Type[Any]) -> Any - assert obj is not None, "call {} on an instance".format(self._fname) + def __get__(self, obj: Any, owner: Type[Any]) -> Any: + assert obj is not None, f"call {self._fname} on an instance" ret = obj.__dict__[self._fname] = self._f(obj) return ret -class LinuxDistribution(object): +class LinuxDistribution: """ Provides information about a OS distribution. @@ -653,13 +662,13 @@ class LinuxDistribution(object): def __init__( self, - include_lsb=True, - os_release_file="", - distro_release_file="", - include_uname=True, - root_dir=None, - ): - # type: (bool, str, str, bool, Optional[str]) -> None + include_lsb: Optional[bool] = None, + os_release_file: str = "", + distro_release_file: str = "", + include_uname: Optional[bool] = None, + root_dir: Optional[str] = None, + include_oslevel: Optional[bool] = None, + ) -> None: """ The initialization method of this class gathers information from the available data sources, and stores that in private instance attributes. @@ -699,7 +708,13 @@ def __init__( be empty. * ``root_dir`` (string): The absolute path to the root directory to use - to find distro-related information files. + to find distro-related information files. Note that ``include_*`` + parameters must not be enabled in combination with ``root_dir``. + + * ``include_oslevel`` (bool): Controls whether (AIX) oslevel command + output is included as a data source. If the oslevel command is not + available in the program execution path the data source will be + empty. Public instance attributes: @@ -718,14 +733,21 @@ def __init__( parameter. This controls whether the uname information will be loaded. + * ``include_oslevel`` (bool): The result of the ``include_oslevel`` + parameter. This controls whether (AIX) oslevel information will be + loaded. + + * ``root_dir`` (string): The result of the ``root_dir`` parameter. + The absolute path to the root directory to use to find distro-related + information files. + Raises: - * :py:exc:`IOError`: Some I/O issue with an os-release file or distro - release file. + * :py:exc:`ValueError`: Initialization parameters combination is not + supported. - * :py:exc:`subprocess.CalledProcessError`: The lsb_release command had - some issue (other than not being available in the program execution - path). + * :py:exc:`OSError`: Some I/O issue with an os-release file or distro + release file. * :py:exc:`UnicodeError`: A data source has unexpected characters or uses an unexpected encoding. @@ -754,11 +776,24 @@ def __init__( self.os_release_file = usr_lib_os_release_file self.distro_release_file = distro_release_file or "" # updated later - self.include_lsb = include_lsb - self.include_uname = include_uname - def __repr__(self): - # type: () -> str + is_root_dir_defined = root_dir is not None + if is_root_dir_defined and (include_lsb or include_uname or include_oslevel): + raise ValueError( + "Including subprocess data sources from specific root_dir is disallowed" + " to prevent false information" + ) + self.include_lsb = ( + include_lsb if include_lsb is not None else not is_root_dir_defined + ) + self.include_uname = ( + include_uname if include_uname is not None else not is_root_dir_defined + ) + self.include_oslevel = ( + include_oslevel if include_oslevel is not None else not is_root_dir_defined + ) + + def __repr__(self) -> str: """Return repr of all info""" return ( "LinuxDistribution(" @@ -766,14 +801,18 @@ def __repr__(self): "distro_release_file={self.distro_release_file!r}, " "include_lsb={self.include_lsb!r}, " "include_uname={self.include_uname!r}, " + "include_oslevel={self.include_oslevel!r}, " + "root_dir={self.root_dir!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) + "_uname_info={self._uname_info!r}, " + "_oslevel_info={self._oslevel_info!r})".format(self=self) ) - def linux_distribution(self, full_distribution_name=True): - # type: (bool) -> Tuple[str, str, str] + def linux_distribution( + self, full_distribution_name: bool = True + ) -> Tuple[str, str, str]: """ Return information about the OS distribution that is compatible with Python's :func:`platform.linux_distribution`, supporting a subset @@ -784,18 +823,16 @@ def linux_distribution(self, full_distribution_name=True): return ( self.name() if full_distribution_name else self.id(), self.version(), - self.codename(), + self._os_release_info.get("release_codename") or self.codename(), ) - def id(self): - # type: () -> str + def id(self) -> str: """Return the distro ID of the OS distribution, as a string. For details, see :func:`distro.id`. """ - def normalize(distro_id, table): - # type: (str, Dict[str, str]) -> str + def normalize(distro_id: str, table: Dict[str, str]) -> str: distro_id = distro_id.lower().replace(" ", "_") return table.get(distro_id, distro_id) @@ -817,8 +854,7 @@ def normalize(distro_id, table): return "" - def name(self, pretty=False): - # type: (bool) -> str + def name(self, pretty: bool = False) -> str: """ Return the name of the OS distribution, as a string. @@ -838,11 +874,10 @@ def name(self, pretty=False): name = self.distro_release_attr("name") or self.uname_attr("name") version = self.version(pretty=True) if version: - name = name + " " + version + name = f"{name} {version}" return name or "" - def version(self, pretty=False, best=False): - # type: (bool, bool) -> str + def version(self, pretty: bool = False, best: bool = False) -> str: """ Return the version of the OS distribution, as a string. @@ -860,6 +895,12 @@ def version(self, pretty=False, best=False): ).get("version_id", ""), self.uname_attr("release"), ] + if self.uname_attr("id").startswith("aix"): + # On AIX platforms, prefer oslevel command output. + versions.insert(0, self.oslevel_info()) + elif self.id() == "debian" or "debian" in self.like().split(): + # On Debian-like, add debian_version file content to candidates list. + versions.append(self._debian_version) version = "" if best: # This algorithm uses the last version in priority order that has @@ -875,11 +916,10 @@ def version(self, pretty=False, best=False): version = v break if pretty and version and self.codename(): - version = "{0} ({1})".format(version, self.codename()) + version = f"{version} ({self.codename()})" return version - def version_parts(self, best=False): - # type: (bool) -> Tuple[str, str, str] + def version_parts(self, best: bool = False) -> Tuple[str, str, str]: """ Return the version of the OS distribution, as a tuple of version numbers. @@ -895,8 +935,7 @@ def version_parts(self, best=False): return major, minor or "", build_number or "" return "", "", "" - def major_version(self, best=False): - # type: (bool) -> str + def major_version(self, best: bool = False) -> str: """ Return the major version number of the current distribution. @@ -904,8 +943,7 @@ def major_version(self, best=False): """ return self.version_parts(best)[0] - def minor_version(self, best=False): - # type: (bool) -> str + def minor_version(self, best: bool = False) -> str: """ Return the minor version number of the current distribution. @@ -913,8 +951,7 @@ def minor_version(self, best=False): """ return self.version_parts(best)[1] - def build_number(self, best=False): - # type: (bool) -> str + def build_number(self, best: bool = False) -> str: """ Return the build number of the current distribution. @@ -922,8 +959,7 @@ def build_number(self, best=False): """ return self.version_parts(best)[2] - def like(self): - # type: () -> str + def like(self) -> str: """ Return the IDs of distributions that are like the OS distribution. @@ -931,8 +967,7 @@ def like(self): """ return self.os_release_attr("id_like") or "" - def codename(self): - # type: () -> str + def codename(self) -> str: """ Return the codename of the OS distribution. @@ -949,8 +984,7 @@ def codename(self): or "" ) - def info(self, pretty=False, best=False): - # type: (bool, bool) -> InfoDict + def info(self, pretty: bool = False, best: bool = False) -> InfoDict: """ Return certain machine-readable information about the OS distribution. @@ -969,8 +1003,7 @@ def info(self, pretty=False, best=False): codename=self.codename(), ) - def os_release_info(self): - # type: () -> Dict[str, str] + def os_release_info(self) -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the os-release file data source of the OS distribution. @@ -979,8 +1012,7 @@ def os_release_info(self): """ return self._os_release_info - def lsb_release_info(self): - # type: () -> Dict[str, str] + def lsb_release_info(self) -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the lsb_release command data source of the OS @@ -990,8 +1022,7 @@ def lsb_release_info(self): """ return self._lsb_release_info - def distro_release_info(self): - # type: () -> Dict[str, str] + def distro_release_info(self) -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the distro release file data source of the OS @@ -1001,8 +1032,7 @@ def distro_release_info(self): """ return self._distro_release_info - def uname_info(self): - # type: () -> Dict[str, str] + def uname_info(self) -> Dict[str, str]: """ Return a dictionary containing key-value pairs for the information items from the uname command data source of the OS distribution. @@ -1011,8 +1041,13 @@ def uname_info(self): """ return self._uname_info - def os_release_attr(self, attribute): - # type: (str) -> str + def oslevel_info(self) -> str: + """ + Return AIX' oslevel command output. + """ + return self._oslevel_info + + def os_release_attr(self, attribute: str) -> str: """ Return a single named information item from the os-release file data source of the OS distribution. @@ -1021,8 +1056,7 @@ def os_release_attr(self, attribute): """ return self._os_release_info.get(attribute, "") - def lsb_release_attr(self, attribute): - # type: (str) -> str + def lsb_release_attr(self, attribute: str) -> str: """ Return a single named information item from the lsb_release command output data source of the OS distribution. @@ -1031,8 +1065,7 @@ def lsb_release_attr(self, attribute): """ return self._lsb_release_info.get(attribute, "") - def distro_release_attr(self, attribute): - # type: (str) -> str + def distro_release_attr(self, attribute: str) -> str: """ Return a single named information item from the distro release file data source of the OS distribution. @@ -1041,8 +1074,7 @@ def distro_release_attr(self, attribute): """ return self._distro_release_info.get(attribute, "") - def uname_attr(self, attribute): - # type: (str) -> str + def uname_attr(self, attribute: str) -> str: """ Return a single named information item from the uname command output data source of the OS distribution. @@ -1052,8 +1084,7 @@ def uname_attr(self, attribute): return self._uname_info.get(attribute, "") @cached_property - def _os_release_info(self): - # type: () -> Dict[str, str] + def _os_release_info(self) -> Dict[str, str]: """ Get the information items from the specified os-release file. @@ -1061,13 +1092,12 @@ def _os_release_info(self): A dictionary containing all information items. """ if os.path.isfile(self.os_release_file): - with open(self.os_release_file) as release_file: + with open(self.os_release_file, encoding="utf-8") as release_file: return self._parse_os_release_content(release_file) return {} @staticmethod - def _parse_os_release_content(lines): - # type: (TextIO) -> Dict[str, str] + def _parse_os_release_content(lines: TextIO) -> Dict[str, str]: """ Parse the lines of an os-release file. @@ -1084,16 +1114,6 @@ def _parse_os_release_content(lines): 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. @@ -1102,12 +1122,17 @@ def _parse_os_release_content(lines): # stripped, etc.), so the tokens are now either: # * variable assignments: var=value # * commands or their arguments (not allowed in os-release) + # Ignore any tokens that are not variable assignments if "=" in token: k, v = token.split("=", 1) props[k.lower()] = v - else: - # Ignore any tokens that are not variable assignments - pass + + if "version" in props: + # extract release codename (if any) from version attribute + match = re.search(r"\((\D+)\)|,\s*(\D+)", props["version"]) + if match: + release_codename = match.group(1) or match.group(2) + props["codename"] = props["release_codename"] = release_codename if "version_codename" in props: # os-release added a version_codename field. Use that in @@ -1118,22 +1143,11 @@ def _parse_os_release_content(lines): 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 - match = re.search(r"(\(\D+\))|,(\s+)?\D+", props["version"]) - if match: - codename = match.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): - # type: () -> Dict[str, str] + def _lsb_release_info(self) -> Dict[str, str]: """ Get the information items from the lsb_release command output. @@ -1142,19 +1156,17 @@ def _lsb_release_info(self): """ if not self.include_lsb: return {} - with open(os.devnull, "wb") as devnull: - try: - cmd = ("lsb_release", "-a") - stdout = subprocess.check_output(cmd, stderr=devnull) - # Command not found or lsb_release returned error - except (OSError, subprocess.CalledProcessError): - return {} + try: + cmd = ("lsb_release", "-a") + stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) + # Command not found or lsb_release returned error + except (OSError, subprocess.CalledProcessError): + return {} content = self._to_str(stdout).splitlines() return self._parse_lsb_release_content(content) @staticmethod - def _parse_lsb_release_content(lines): - # type: (Iterable[str]) -> Dict[str, str] + def _parse_lsb_release_content(lines: Iterable[str]) -> Dict[str, str]: """ Parse the output of the lsb_release command. @@ -1178,20 +1190,41 @@ def _parse_lsb_release_content(lines): return props @cached_property - def _uname_info(self): - # type: () -> Dict[str, str] - with open(os.devnull, "wb") as devnull: - try: - cmd = ("uname", "-rs") - stdout = subprocess.check_output(cmd, stderr=devnull) - except OSError: - return {} + def _uname_info(self) -> Dict[str, str]: + if not self.include_uname: + return {} + try: + cmd = ("uname", "-rs") + stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) + except OSError: + return {} content = self._to_str(stdout).splitlines() return self._parse_uname_content(content) + @cached_property + def _oslevel_info(self) -> str: + if not self.include_oslevel: + return "" + try: + stdout = subprocess.check_output("oslevel", stderr=subprocess.DEVNULL) + except (OSError, subprocess.CalledProcessError): + return "" + return self._to_str(stdout).strip() + + @cached_property + def _debian_version(self) -> str: + try: + with open( + os.path.join(self.etc_dir, "debian_version"), encoding="ascii" + ) as fp: + return fp.readline().rstrip() + except FileNotFoundError: + return "" + @staticmethod - def _parse_uname_content(lines): - # type: (Sequence[str]) -> Dict[str, str] + def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]: + if not lines: + return {} props = {} match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip()) if match: @@ -1208,23 +1241,12 @@ def _parse_uname_content(lines): return props @staticmethod - def _to_str(text): - # type: (Union[bytes, str]) -> str + def _to_str(bytestring: bytes) -> str: encoding = sys.getfilesystemencoding() - encoding = "utf-8" if encoding == "ascii" else encoding - - if sys.version_info[0] >= 3: - if isinstance(text, bytes): - return text.decode(encoding) - else: - if isinstance(text, unicode): # noqa - return text.encode(encoding) - - return text + return bytestring.decode(encoding) @cached_property - def _distro_release_info(self): - # type: () -> Dict[str, str] + def _distro_release_info(self) -> Dict[str, str]: """ Get the information items from the specified distro release file. @@ -1241,14 +1263,14 @@ def _distro_release_info(self): # 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(self.etc_dir) + basenames = [ + basename + for basename in os.listdir(self.etc_dir) + if basename not in _DISTRO_RELEASE_IGNORE_BASENAMES + and os.path.isfile(os.path.join(self.etc_dir, basename)) + ] # 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. @@ -1258,41 +1280,31 @@ def _distro_release_info(self): # 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", - ] + basenames = _DISTRO_RELEASE_BASENAMES 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(self.etc_dir, 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 {} + if match is None: + continue + filepath = os.path.join(self.etc_dir, basename) + distro_info = self._parse_distro_release_file(filepath) + # The name is always present if the pattern matches. + if "name" not in distro_info: + continue + self.distro_release_file = filepath + break + else: # the loop didn't "break": no candidate. + return {} + + if match is not None: + distro_info["id"] = match.group(1) + + # CloudLinux < 7: manually enrich info with proper id. + if "cloudlinux" in distro_info.get("name", "").lower(): + distro_info["id"] = "cloudlinux" + + return distro_info - def _parse_distro_release_file(self, filepath): - # type: (str) -> Dict[str, str] + def _parse_distro_release_file(self, filepath: str) -> Dict[str, str]: """ Parse a distro release file. @@ -1304,19 +1316,18 @@ def _parse_distro_release_file(self, filepath): A dictionary containing all information items. """ try: - with open(filepath) as fp: + with open(filepath, encoding="utf-8") 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): + except OSError: # Ignore not being able to read a specific, seemingly version # related file. # See https://github.com/python-distro/distro/issues/162 return {} @staticmethod - def _parse_distro_release_content(line): - # type: (str) -> Dict[str, str] + def _parse_distro_release_content(line: str) -> Dict[str, str]: """ Parse a line from a distro release file. @@ -1344,8 +1355,7 @@ def _parse_distro_release_content(line): _distro = LinuxDistribution() -def main(): - # type: () -> None +def main() -> None: logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler(sys.stdout)) @@ -1367,7 +1377,10 @@ def main(): if args.root_dir: dist = LinuxDistribution( - include_lsb=False, include_uname=False, root_dir=args.root_dir + include_lsb=False, + include_uname=False, + include_oslevel=False, + root_dir=args.root_dir, ) else: dist = _distro diff --git a/src/distro/py.typed b/src/distro/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/tests/resources/distros/aix72/bin/oslevel b/tests/resources/distros/aix72/bin/oslevel new file mode 100755 index 0000000..a8e9e38 --- /dev/null +++ b/tests/resources/distros/aix72/bin/oslevel @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "7.2.0.0" diff --git a/tests/resources/distros/aix72/bin/uname b/tests/resources/distros/aix72/bin/uname new file mode 100755 index 0000000..c3d37f8 --- /dev/null +++ b/tests/resources/distros/aix72/bin/uname @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "AIX 2" diff --git a/tests/resources/distros/arch/etc/os-release b/tests/resources/distros/arch/etc/os-release deleted file mode 100644 index 30e5cf3..0000000 --- a/tests/resources/distros/arch/etc/os-release +++ /dev/null @@ -1,7 +0,0 @@ -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/tests/resources/distros/arch/etc/os-release b/tests/resources/distros/arch/etc/os-release new file mode 120000 index 0000000..c4c75b4 --- /dev/null +++ b/tests/resources/distros/arch/etc/os-release @@ -0,0 +1 @@ +../usr/lib/os-release \ No newline at end of file diff --git a/tests/resources/distros/buildroot/etc/os-release b/tests/resources/distros/buildroot/etc/os-release new file mode 100644 index 0000000..3aeb531 --- /dev/null +++ b/tests/resources/distros/buildroot/etc/os-release @@ -0,0 +1,5 @@ +NAME=Buildroot +VERSION=2022.02 +ID=buildroot +VERSION_ID=2022.02 +PRETTY_NAME="Buildroot 2022.02" diff --git a/tests/resources/distros/centos5/etc/redhat-release b/tests/resources/distros/centos5/etc/redhat-release deleted file mode 100644 index 7aade1d..0000000 --- a/tests/resources/distros/centos5/etc/redhat-release +++ /dev/null @@ -1 +0,0 @@ -CentOS release 5.11 (Final) diff --git a/tests/resources/distros/centos5/etc/redhat-release b/tests/resources/distros/centos5/etc/redhat-release new file mode 120000 index 0000000..05b34c7 --- /dev/null +++ b/tests/resources/distros/centos5/etc/redhat-release @@ -0,0 +1 @@ +centos-release \ No newline at end of file diff --git a/tests/resources/distros/centos5/etc/system-release b/tests/resources/distros/centos5/etc/system-release deleted file mode 100644 index 7aade1d..0000000 --- a/tests/resources/distros/centos5/etc/system-release +++ /dev/null @@ -1 +0,0 @@ -CentOS release 5.11 (Final) diff --git a/tests/resources/distros/centos5/etc/system-release b/tests/resources/distros/centos5/etc/system-release new file mode 120000 index 0000000..05b34c7 --- /dev/null +++ b/tests/resources/distros/centos5/etc/system-release @@ -0,0 +1 @@ +centos-release \ No newline at end of file diff --git a/tests/resources/distros/centos7/etc/redhat-release b/tests/resources/distros/centos7/etc/redhat-release deleted file mode 100644 index 6254a91..0000000 --- a/tests/resources/distros/centos7/etc/redhat-release +++ /dev/null @@ -1 +0,0 @@ -CentOS Linux release 7.1.1503 (Core) diff --git a/tests/resources/distros/centos7/etc/redhat-release b/tests/resources/distros/centos7/etc/redhat-release new file mode 120000 index 0000000..05b34c7 --- /dev/null +++ b/tests/resources/distros/centos7/etc/redhat-release @@ -0,0 +1 @@ +centos-release \ No newline at end of file diff --git a/tests/resources/distros/centos7/etc/system-release b/tests/resources/distros/centos7/etc/system-release deleted file mode 100644 index 6254a91..0000000 --- a/tests/resources/distros/centos7/etc/system-release +++ /dev/null @@ -1 +0,0 @@ -CentOS Linux release 7.1.1503 (Core) diff --git a/tests/resources/distros/centos7/etc/system-release b/tests/resources/distros/centos7/etc/system-release new file mode 120000 index 0000000..05b34c7 --- /dev/null +++ b/tests/resources/distros/centos7/etc/system-release @@ -0,0 +1 @@ +centos-release \ No newline at end of file diff --git a/tests/resources/distros/debian10/bin/lsb_release b/tests/resources/distros/debian10/bin/lsb_release new file mode 100644 index 0000000..7d269dc --- /dev/null +++ b/tests/resources/distros/debian10/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 10 (buster)" +echo "Release: 10" +echo "Codename: buster" + +exit 0 diff --git a/tests/resources/distros/debian10/etc/debian_version b/tests/resources/distros/debian10/etc/debian_version new file mode 100644 index 0000000..7f0c091 --- /dev/null +++ b/tests/resources/distros/debian10/etc/debian_version @@ -0,0 +1 @@ +10.11 diff --git a/tests/resources/distros/debian10/etc/os-release b/tests/resources/distros/debian10/etc/os-release new file mode 100644 index 0000000..9b5419d --- /dev/null +++ b/tests/resources/distros/debian10/etc/os-release @@ -0,0 +1,9 @@ +PRETTY_NAME="Debian GNU/Linux 10 (buster)" +NAME="Debian GNU/Linux" +VERSION_ID="10" +VERSION="10 (buster)" +VERSION_CODENAME=buster +ID=debian +HOME_URL="https://www.debian.org/" +SUPPORT_URL="https://www.debian.org/support" +BUG_REPORT_URL="https://bugs.debian.org/" diff --git a/tests/resources/distros/fedora19/etc/redhat-release b/tests/resources/distros/fedora19/etc/redhat-release deleted file mode 100644 index bebc20b..0000000 --- a/tests/resources/distros/fedora19/etc/redhat-release +++ /dev/null @@ -1 +0,0 @@ -Fedora release 19 (Schrödinger’s Cat) diff --git a/tests/resources/distros/fedora19/etc/redhat-release b/tests/resources/distros/fedora19/etc/redhat-release new file mode 120000 index 0000000..fc49fa6 --- /dev/null +++ b/tests/resources/distros/fedora19/etc/redhat-release @@ -0,0 +1 @@ +fedora-release \ No newline at end of file diff --git a/tests/resources/distros/fedora19/etc/system-release b/tests/resources/distros/fedora19/etc/system-release deleted file mode 100644 index bebc20b..0000000 --- a/tests/resources/distros/fedora19/etc/system-release +++ /dev/null @@ -1 +0,0 @@ -Fedora release 19 (Schrödinger’s Cat) diff --git a/tests/resources/distros/fedora19/etc/system-release b/tests/resources/distros/fedora19/etc/system-release new file mode 120000 index 0000000..fc49fa6 --- /dev/null +++ b/tests/resources/distros/fedora19/etc/system-release @@ -0,0 +1 @@ +fedora-release \ No newline at end of file diff --git a/tests/resources/distros/fedora23/etc/os-release b/tests/resources/distros/fedora23/etc/os-release deleted file mode 100644 index f57b5fd..0000000 --- a/tests/resources/distros/fedora23/etc/os-release +++ /dev/null @@ -1,14 +0,0 @@ -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/tests/resources/distros/fedora23/etc/os-release b/tests/resources/distros/fedora23/etc/os-release new file mode 120000 index 0000000..c4c75b4 --- /dev/null +++ b/tests/resources/distros/fedora23/etc/os-release @@ -0,0 +1 @@ +../usr/lib/os-release \ No newline at end of file diff --git a/tests/resources/distros/fedora23/etc/redhat-release b/tests/resources/distros/fedora23/etc/redhat-release deleted file mode 100644 index 53d352f..0000000 --- a/tests/resources/distros/fedora23/etc/redhat-release +++ /dev/null @@ -1 +0,0 @@ -Fedora release 23 (Twenty Three) diff --git a/tests/resources/distros/fedora23/etc/redhat-release b/tests/resources/distros/fedora23/etc/redhat-release new file mode 120000 index 0000000..fc49fa6 --- /dev/null +++ b/tests/resources/distros/fedora23/etc/redhat-release @@ -0,0 +1 @@ +fedora-release \ No newline at end of file diff --git a/tests/resources/distros/fedora23/etc/system-release b/tests/resources/distros/fedora23/etc/system-release deleted file mode 100644 index 53d352f..0000000 --- a/tests/resources/distros/fedora23/etc/system-release +++ /dev/null @@ -1 +0,0 @@ -Fedora release 23 (Twenty Three) diff --git a/tests/resources/distros/fedora23/etc/system-release b/tests/resources/distros/fedora23/etc/system-release new file mode 120000 index 0000000..fc49fa6 --- /dev/null +++ b/tests/resources/distros/fedora23/etc/system-release @@ -0,0 +1 @@ +fedora-release \ No newline at end of file diff --git a/tests/resources/distros/fedora30/etc/os-release b/tests/resources/distros/fedora30/etc/os-release deleted file mode 100644 index 2a82709..0000000 --- a/tests/resources/distros/fedora30/etc/os-release +++ /dev/null @@ -1,19 +0,0 @@ -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/tests/resources/distros/fedora30/etc/os-release b/tests/resources/distros/fedora30/etc/os-release new file mode 120000 index 0000000..c4c75b4 --- /dev/null +++ b/tests/resources/distros/fedora30/etc/os-release @@ -0,0 +1 @@ +../usr/lib/os-release \ No newline at end of file diff --git a/tests/resources/distros/fedora30/etc/redhat-release b/tests/resources/distros/fedora30/etc/redhat-release deleted file mode 100644 index 88caaa8..0000000 --- a/tests/resources/distros/fedora30/etc/redhat-release +++ /dev/null @@ -1 +0,0 @@ -Fedora release 30 (Thirty) diff --git a/tests/resources/distros/fedora30/etc/redhat-release b/tests/resources/distros/fedora30/etc/redhat-release new file mode 120000 index 0000000..fc49fa6 --- /dev/null +++ b/tests/resources/distros/fedora30/etc/redhat-release @@ -0,0 +1 @@ +fedora-release \ No newline at end of file diff --git a/tests/resources/distros/fedora30/etc/system-release b/tests/resources/distros/fedora30/etc/system-release deleted file mode 100644 index 88caaa8..0000000 --- a/tests/resources/distros/fedora30/etc/system-release +++ /dev/null @@ -1 +0,0 @@ -Fedora release 30 (Thirty) diff --git a/tests/resources/distros/fedora30/etc/system-release b/tests/resources/distros/fedora30/etc/system-release new file mode 120000 index 0000000..fc49fa6 --- /dev/null +++ b/tests/resources/distros/fedora30/etc/system-release @@ -0,0 +1 @@ +fedora-release \ No newline at end of file diff --git a/tests/resources/distros/guix/etc/os-release b/tests/resources/distros/guix/etc/os-release new file mode 100644 index 0000000..8aa5b73 --- /dev/null +++ b/tests/resources/distros/guix/etc/os-release @@ -0,0 +1,8 @@ +NAME="Guix System" +ID=guix +PRETTY_NAME="Guix System" +LOGO=guix-icon +HOME_URL="https://guix.gnu.org" +DOCUMENTATION_URL="https://guix.gnu.org/en/manual" +SUPPORT_URL="https://guix.gnu.org/en/help" +BUG_REPORT_URL="https://lists.gnu.org/mailman/listinfo/bug-guix" diff --git a/tests/resources/distros/kvmibm1/etc/redhat-release b/tests/resources/distros/kvmibm1/etc/redhat-release deleted file mode 100644 index 1a3157c..0000000 --- a/tests/resources/distros/kvmibm1/etc/redhat-release +++ /dev/null @@ -1 +0,0 @@ -KVM for IBM z Systems release 1.1.1 (Z) diff --git a/tests/resources/distros/kvmibm1/etc/redhat-release b/tests/resources/distros/kvmibm1/etc/redhat-release new file mode 120000 index 0000000..63f7c55 --- /dev/null +++ b/tests/resources/distros/kvmibm1/etc/redhat-release @@ -0,0 +1 @@ +base-release \ No newline at end of file diff --git a/tests/resources/distros/kvmibm1/etc/system-release b/tests/resources/distros/kvmibm1/etc/system-release deleted file mode 100644 index 1a3157c..0000000 --- a/tests/resources/distros/kvmibm1/etc/system-release +++ /dev/null @@ -1 +0,0 @@ -KVM for IBM z Systems release 1.1.1 (Z) diff --git a/tests/resources/distros/kvmibm1/etc/system-release b/tests/resources/distros/kvmibm1/etc/system-release new file mode 120000 index 0000000..63f7c55 --- /dev/null +++ b/tests/resources/distros/kvmibm1/etc/system-release @@ -0,0 +1 @@ +base-release \ No newline at end of file diff --git a/tests/resources/distros/linuxmint17/bin/lsb_release b/tests/resources/distros/linuxmint17/bin/lsb_release deleted file mode 100755 index 892d5e6..0000000 --- a/tests/resources/distros/linuxmint17/bin/lsb_release +++ /dev/null @@ -1,43 +0,0 @@ -#!/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/tests/resources/distros/linuxmint17/bin/lsb_release b/tests/resources/distros/linuxmint17/bin/lsb_release new file mode 120000 index 0000000..08de9fb --- /dev/null +++ b/tests/resources/distros/linuxmint17/bin/lsb_release @@ -0,0 +1 @@ +../../__shared__/bin/lsb_release \ No newline at end of file diff --git a/tests/resources/distros/mageia5/etc/mandrake-release b/tests/resources/distros/mageia5/etc/mandrake-release deleted file mode 100644 index 5dfbfd2..0000000 --- a/tests/resources/distros/mageia5/etc/mandrake-release +++ /dev/null @@ -1 +0,0 @@ -Mageia release 5 (Official) for x86_64 diff --git a/tests/resources/distros/mageia5/etc/mandrake-release b/tests/resources/distros/mageia5/etc/mandrake-release new file mode 120000 index 0000000..1fbd004 --- /dev/null +++ b/tests/resources/distros/mageia5/etc/mandrake-release @@ -0,0 +1 @@ +mageia-release \ No newline at end of file diff --git a/tests/resources/distros/mageia5/etc/mandrakelinux-release b/tests/resources/distros/mageia5/etc/mandrakelinux-release deleted file mode 100644 index 5dfbfd2..0000000 --- a/tests/resources/distros/mageia5/etc/mandrakelinux-release +++ /dev/null @@ -1 +0,0 @@ -Mageia release 5 (Official) for x86_64 diff --git a/tests/resources/distros/mageia5/etc/mandrakelinux-release b/tests/resources/distros/mageia5/etc/mandrakelinux-release new file mode 120000 index 0000000..1fbd004 --- /dev/null +++ b/tests/resources/distros/mageia5/etc/mandrakelinux-release @@ -0,0 +1 @@ +mageia-release \ No newline at end of file diff --git a/tests/resources/distros/mageia5/etc/mandriva-release b/tests/resources/distros/mageia5/etc/mandriva-release deleted file mode 100644 index 5dfbfd2..0000000 --- a/tests/resources/distros/mageia5/etc/mandriva-release +++ /dev/null @@ -1 +0,0 @@ -Mageia release 5 (Official) for x86_64 diff --git a/tests/resources/distros/mageia5/etc/mandriva-release b/tests/resources/distros/mageia5/etc/mandriva-release new file mode 120000 index 0000000..1fbd004 --- /dev/null +++ b/tests/resources/distros/mageia5/etc/mandriva-release @@ -0,0 +1 @@ +mageia-release \ No newline at end of file diff --git a/tests/resources/distros/mageia5/etc/os-release b/tests/resources/distros/mageia5/etc/os-release deleted file mode 100644 index a581d99..0000000 --- a/tests/resources/distros/mageia5/etc/os-release +++ /dev/null @@ -1,10 +0,0 @@ -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/tests/resources/distros/mageia5/etc/os-release b/tests/resources/distros/mageia5/etc/os-release new file mode 120000 index 0000000..c4c75b4 --- /dev/null +++ b/tests/resources/distros/mageia5/etc/os-release @@ -0,0 +1 @@ +../usr/lib/os-release \ No newline at end of file diff --git a/tests/resources/distros/mageia5/etc/redhat-release b/tests/resources/distros/mageia5/etc/redhat-release deleted file mode 100644 index 5dfbfd2..0000000 --- a/tests/resources/distros/mageia5/etc/redhat-release +++ /dev/null @@ -1 +0,0 @@ -Mageia release 5 (Official) for x86_64 diff --git a/tests/resources/distros/mageia5/etc/redhat-release b/tests/resources/distros/mageia5/etc/redhat-release new file mode 120000 index 0000000..1fbd004 --- /dev/null +++ b/tests/resources/distros/mageia5/etc/redhat-release @@ -0,0 +1 @@ +mageia-release \ No newline at end of file diff --git a/tests/resources/distros/mageia5/etc/release b/tests/resources/distros/mageia5/etc/release deleted file mode 100644 index 5dfbfd2..0000000 --- a/tests/resources/distros/mageia5/etc/release +++ /dev/null @@ -1 +0,0 @@ -Mageia release 5 (Official) for x86_64 diff --git a/tests/resources/distros/mageia5/etc/release b/tests/resources/distros/mageia5/etc/release new file mode 120000 index 0000000..1fbd004 --- /dev/null +++ b/tests/resources/distros/mageia5/etc/release @@ -0,0 +1 @@ +mageia-release \ No newline at end of file diff --git a/tests/resources/distros/mandriva2011/etc/mandrake-release b/tests/resources/distros/mandriva2011/etc/mandrake-release deleted file mode 100644 index 099de71..0000000 --- a/tests/resources/distros/mandriva2011/etc/mandrake-release +++ /dev/null @@ -1 +0,0 @@ -Mandriva Linux release 2011.0 (Official) for x86_64 diff --git a/tests/resources/distros/mandriva2011/etc/mandrake-release b/tests/resources/distros/mandriva2011/etc/mandrake-release new file mode 120000 index 0000000..a8948ae --- /dev/null +++ b/tests/resources/distros/mandriva2011/etc/mandrake-release @@ -0,0 +1 @@ +mandriva-release \ No newline at end of file diff --git a/tests/resources/distros/mandriva2011/etc/mandrakelinux-release b/tests/resources/distros/mandriva2011/etc/mandrakelinux-release deleted file mode 100644 index 099de71..0000000 --- a/tests/resources/distros/mandriva2011/etc/mandrakelinux-release +++ /dev/null @@ -1 +0,0 @@ -Mandriva Linux release 2011.0 (Official) for x86_64 diff --git a/tests/resources/distros/mandriva2011/etc/mandrakelinux-release b/tests/resources/distros/mandriva2011/etc/mandrakelinux-release new file mode 120000 index 0000000..a8948ae --- /dev/null +++ b/tests/resources/distros/mandriva2011/etc/mandrakelinux-release @@ -0,0 +1 @@ +mandriva-release \ No newline at end of file diff --git a/tests/resources/distros/mandriva2011/etc/redhat-release b/tests/resources/distros/mandriva2011/etc/redhat-release deleted file mode 100644 index 099de71..0000000 --- a/tests/resources/distros/mandriva2011/etc/redhat-release +++ /dev/null @@ -1 +0,0 @@ -Mandriva Linux release 2011.0 (Official) for x86_64 diff --git a/tests/resources/distros/mandriva2011/etc/redhat-release b/tests/resources/distros/mandriva2011/etc/redhat-release new file mode 120000 index 0000000..a8948ae --- /dev/null +++ b/tests/resources/distros/mandriva2011/etc/redhat-release @@ -0,0 +1 @@ +mandriva-release \ No newline at end of file diff --git a/tests/resources/distros/mandriva2011/etc/release b/tests/resources/distros/mandriva2011/etc/release deleted file mode 100644 index 099de71..0000000 --- a/tests/resources/distros/mandriva2011/etc/release +++ /dev/null @@ -1 +0,0 @@ -Mandriva Linux release 2011.0 (Official) for x86_64 diff --git a/tests/resources/distros/mandriva2011/etc/release b/tests/resources/distros/mandriva2011/etc/release new file mode 120000 index 0000000..a8948ae --- /dev/null +++ b/tests/resources/distros/mandriva2011/etc/release @@ -0,0 +1 @@ +mandriva-release \ No newline at end of file diff --git a/tests/resources/distros/manjaro1512/bin/lsb_release b/tests/resources/distros/manjaro1512/bin/lsb_release deleted file mode 100755 index 892d5e6..0000000 --- a/tests/resources/distros/manjaro1512/bin/lsb_release +++ /dev/null @@ -1,43 +0,0 @@ -#!/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/tests/resources/distros/manjaro1512/bin/lsb_release b/tests/resources/distros/manjaro1512/bin/lsb_release new file mode 120000 index 0000000..08de9fb --- /dev/null +++ b/tests/resources/distros/manjaro1512/bin/lsb_release @@ -0,0 +1 @@ +../../__shared__/bin/lsb_release \ No newline at end of file diff --git a/tests/resources/distros/opensuse15/etc/os-release b/tests/resources/distros/opensuse15/etc/os-release new file mode 100644 index 0000000..1494bc5 --- /dev/null +++ b/tests/resources/distros/opensuse15/etc/os-release @@ -0,0 +1,10 @@ +NAME="openSUSE Leap" +VERSION="15.2" +ID="opensuse-leap" +ID_LIKE="suse opensuse" +VERSION_ID="15.2" +PRETTY_NAME="openSUSE Leap 15.2" +ANSI_COLOR="0;32" +CPE_NAME="cpe:/o:opensuse:leap:15.2" +BUG_REPORT_URL="https://bugs.opensuse.org" +HOME_URL="https://www.opensuse.org/" diff --git a/tests/resources/distros/raspbian7/etc/os-release.orig b/tests/resources/distros/raspbian7/etc/os-release.orig deleted file mode 100644 index d72d284..0000000 --- a/tests/resources/distros/raspbian7/etc/os-release.orig +++ /dev/null @@ -1,9 +0,0 @@ -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/tests/resources/distros/rhel6/etc/system-release b/tests/resources/distros/rhel6/etc/system-release deleted file mode 100644 index c90ee5f..0000000 --- a/tests/resources/distros/rhel6/etc/system-release +++ /dev/null @@ -1 +0,0 @@ -Red Hat Enterprise Linux Server release 6.5 (Santiago) diff --git a/tests/resources/distros/rhel6/etc/system-release b/tests/resources/distros/rhel6/etc/system-release new file mode 120000 index 0000000..428ee09 --- /dev/null +++ b/tests/resources/distros/rhel6/etc/system-release @@ -0,0 +1 @@ +redhat-release \ No newline at end of file diff --git a/tests/resources/distros/rhel7/etc/system-release b/tests/resources/distros/rhel7/etc/system-release deleted file mode 100644 index ef10e5a..0000000 --- a/tests/resources/distros/rhel7/etc/system-release +++ /dev/null @@ -1 +0,0 @@ -Red Hat Enterprise Linux Server release 7.0 (Maipo) diff --git a/tests/resources/distros/rhel7/etc/system-release b/tests/resources/distros/rhel7/etc/system-release new file mode 120000 index 0000000..428ee09 --- /dev/null +++ b/tests/resources/distros/rhel7/etc/system-release @@ -0,0 +1 @@ +redhat-release \ No newline at end of file diff --git a/tests/resources/distros/rocky/etc/centos-release b/tests/resources/distros/rocky/etc/centos-release new file mode 120000 index 0000000..780b78a --- /dev/null +++ b/tests/resources/distros/rocky/etc/centos-release @@ -0,0 +1 @@ +rocky-release \ No newline at end of file diff --git a/tests/resources/distros/rocky/etc/os-release b/tests/resources/distros/rocky/etc/os-release new file mode 120000 index 0000000..c4c75b4 --- /dev/null +++ b/tests/resources/distros/rocky/etc/os-release @@ -0,0 +1 @@ +../usr/lib/os-release \ No newline at end of file diff --git a/tests/resources/distros/rocky/etc/redhat-release b/tests/resources/distros/rocky/etc/redhat-release new file mode 120000 index 0000000..780b78a --- /dev/null +++ b/tests/resources/distros/rocky/etc/redhat-release @@ -0,0 +1 @@ +rocky-release \ No newline at end of file diff --git a/tests/resources/distros/rocky/etc/rocky-release b/tests/resources/distros/rocky/etc/rocky-release new file mode 100644 index 0000000..b732321 --- /dev/null +++ b/tests/resources/distros/rocky/etc/rocky-release @@ -0,0 +1 @@ +Rocky Linux release 8.4 (Green Obsidian) diff --git a/tests/resources/distros/rocky/etc/rocky-release-upstream b/tests/resources/distros/rocky/etc/rocky-release-upstream new file mode 100644 index 0000000..5d7b282 --- /dev/null +++ b/tests/resources/distros/rocky/etc/rocky-release-upstream @@ -0,0 +1 @@ +Derived from Red Hat Enterprise Linux 8.4 diff --git a/tests/resources/distros/rocky/etc/system-release b/tests/resources/distros/rocky/etc/system-release new file mode 120000 index 0000000..780b78a --- /dev/null +++ b/tests/resources/distros/rocky/etc/system-release @@ -0,0 +1 @@ +rocky-release \ No newline at end of file diff --git a/tests/resources/distros/rocky/etc/system-release-cpe b/tests/resources/distros/rocky/etc/system-release-cpe new file mode 100644 index 0000000..741db58 --- /dev/null +++ b/tests/resources/distros/rocky/etc/system-release-cpe @@ -0,0 +1 @@ +cpe:/o:rocky:rocky:8.4:GA diff --git a/tests/resources/distros/rocky/usr/lib/os-release b/tests/resources/distros/rocky/usr/lib/os-release new file mode 100644 index 0000000..f753b05 --- /dev/null +++ b/tests/resources/distros/rocky/usr/lib/os-release @@ -0,0 +1,13 @@ +NAME="Rocky Linux" +VERSION="8.4 (Green Obsidian)" +ID="rocky" +ID_LIKE="rhel centos fedora" +VERSION_ID="8.4" +PLATFORM_ID="platform:el8" +PRETTY_NAME="Rocky Linux 8.4 (Green Obsidian)" +ANSI_COLOR="0;32" +CPE_NAME="cpe:/o:rocky:rocky:8.4:GA" +HOME_URL="https://rockylinux.org/" +BUG_REPORT_URL="https://bugs.rockylinux.org/" +ROCKY_SUPPORT_PRODUCT="Rocky Linux" +ROCKY_SUPPORT_PRODUCT_VERSION="8" diff --git a/tests/resources/testdistros/distro/dontincludeuname/bin/uname b/tests/resources/testdistros/distro/dontincludeuname/bin/uname new file mode 100644 index 0000000..2ea1358 --- /dev/null +++ b/tests/resources/testdistros/distro/dontincludeuname/bin/uname @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "NetBSD 7.1.1" diff --git a/tests/resources/testdistros/distro/emptyuname/bin/uname b/tests/resources/testdistros/distro/emptyuname/bin/uname new file mode 100644 index 0000000..58a4484 --- /dev/null +++ b/tests/resources/testdistros/distro/emptyuname/bin/uname @@ -0,0 +1,3 @@ +#!/bin/sh + +echo -n diff --git a/tests/test_distro.py b/tests/test_distro.py index b51ace9..3dda970 100644 --- a/tests/test_distro.py +++ b/tests/test_distro.py @@ -1,4 +1,3 @@ -# coding=utf-8 # Copyright 2015,2016 Nir Cohen # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,15 +13,13 @@ # limitations under the License. import ast +import io import json import os import subprocess import sys - -try: - from StringIO import StringIO # Python 2.x -except ImportError: - from io import StringIO # Python 3.x +from types import FunctionType +from typing import Any, Dict, List, NoReturn, Optional import pytest @@ -36,7 +33,7 @@ IS_LINUX = sys.platform.startswith("linux") if IS_LINUX: - import distro + from distro import distro RELATIVE_UNIXCONFDIR = distro._UNIXCONFDIR[1:] RELATIVE_UNIXUSRLIBDIR = distro._UNIXUSRLIBDIR[1:] @@ -44,7 +41,7 @@ class TestNonLinuxPlatform: - def test_cant_use_on_windows(self): + def test_cant_use_on_windows(self) -> None: try: import distro # NOQA except ImportError as ex: @@ -53,46 +50,48 @@ def test_cant_use_on_windows(self): @pytest.mark.skipif(not IS_LINUX, reason="Irrelevant on non-linux") class TestCli: - def _parse(self, command): + def _parse(self, command: str) -> None: 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 _run(self, command: List[str]) -> str: + r = subprocess.run( + command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + return r.stdout - def test_cli_for_coverage_yuch(self): + def test_cli_for_coverage_yuch(self) -> None: self._parse("distro") self._parse("distro -j") - def test_cli_can_parse_root_dir_args(self): + def test_cli_can_parse_root_dir_args(self) -> None: root_dir = os.path.join(RESOURCES, "cli", "fedora30") - self._parse("distro --root-dir {}".format(root_dir)) + self._parse(f"distro --root-dir {root_dir}") - def test_cli(self): + def test_cli(self) -> None: command = [sys.executable, "-m", "distro"] - desired_output = "Name: " + distro.name(pretty=True) + desired_output = f"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 += f"\nVersion: {distro_version}" + desired_output += f"\nCodename: {distro_codename}" desired_output += "\n" assert self._run(command) == desired_output - def test_cli_json(self): + def test_cli_json(self) -> None: command = [sys.executable, "-m", "distro", "-j"] assert ast.literal_eval(self._run(command)) == distro.info() - def test_cli_with_root_dir(self): + def test_cli_with_root_dir(self) -> None: root_dir = os.path.join(RESOURCES, "cli", "fedora30") command = [sys.executable, "-m", "distro", "--root-dir", root_dir] desired_output = "Name: Fedora 30 (Thirty)\nVersion: 30\nCodename: \n" assert desired_output == self._run(command) - def test_cli_with_root_dir_as_json(self): + def test_cli_with_root_dir_as_json(self) -> None: root_dir = os.path.join(RESOURCES, "cli", "fedora30") command = [sys.executable, "-m", "distro", "-j", "--root-dir", root_dir] desired_output = { @@ -108,12 +107,12 @@ def test_cli_with_root_dir_as_json(self): @pytest.mark.skipif(not IS_LINUX, reason="Irrelevant on non-linux") -class DistroTestCase(object): +class DistroTestCase: """A base class for any testcase classes that test the distributions represented in the `DISTROS` subtree. """ - def setup_method(self, test_method): + def setup_method(self, test_method: FunctionType) -> None: # The environment stays the same across all testcases, so we # save and restore the PATH env var in each test case that # changes it: @@ -121,12 +120,12 @@ def setup_method(self, test_method): self._saved_UNIXCONFDIR = distro._UNIXCONFDIR self._saved_UNIXUSRLIBDIR = distro._UNIXUSRLIBDIR - def teardown_method(self, test_method): + def teardown_method(self, test_method: FunctionType) -> None: os.environ["PATH"] = self._saved_path distro._UNIXCONFDIR = self._saved_UNIXCONFDIR distro._UNIXUSRLIBDIR = self._saved_UNIXUSRLIBDIR - def _setup_for_distro(self, distro_root): + def _setup_for_distro(self, distro_root: str) -> None: 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: @@ -137,16 +136,15 @@ def _setup_for_distro(self, distro_root): @pytest.mark.skipif(not IS_LINUX, reason="Irrelevant on non-linux") class TestOSRelease: - def setup_method(self, test_method): + def setup_method(self, test_method: FunctionType) -> None: dist = test_method.__name__.split("_")[1] - os_release = os.path.join(DISTROS_DIR, dist, "etc", "os-release") self.distro = distro.LinuxDistribution( include_lsb=False, - os_release_file=os_release, distro_release_file="path-to-non-existing-file", + root_dir=os.path.join(DISTROS_DIR, dist), ) - def _test_outcome(self, outcome): + def _test_outcome(self, outcome: Dict[str, str]) -> None: assert self.distro.id() == outcome.get("id", "") assert self.distro.name() == outcome.get("name", "") assert self.distro.name(pretty=True) == outcome.get("pretty_name", "") @@ -156,7 +154,7 @@ def _test_outcome(self, outcome): assert self.distro.like() == outcome.get("like", "") assert self.distro.codename() == outcome.get("codename", "") - def test_arch_os_release(self): + def test_arch_os_release(self) -> None: desired_outcome = { "id": "arch", "name": "Arch Linux", @@ -164,7 +162,18 @@ def test_arch_os_release(self): } self._test_outcome(desired_outcome) - def test_kali_os_release(self): + def test_buildroot_os_release(self) -> None: + desired_outcome = { + "id": "buildroot", + "name": "Buildroot", + "pretty_name": "Buildroot 2022.02", + "version": "2022.02", + "pretty_version": "2022.02", + "best_version": "2022.02", + } + self._test_outcome(desired_outcome) + + def test_kali_os_release(self) -> None: desired_outcome = { "id": "kali", "name": "Kali GNU/Linux", @@ -176,7 +185,7 @@ def test_kali_os_release(self): } self._test_outcome(desired_outcome) - def test_centos7_os_release(self): + def test_centos7_os_release(self) -> None: desired_outcome = { "id": "centos", "name": "CentOS Linux", @@ -189,7 +198,7 @@ def test_centos7_os_release(self): } self._test_outcome(desired_outcome) - def test_coreos_os_release(self): + def test_coreos_os_release(self) -> None: desired_outcome = { "id": "coreos", "name": "CoreOS", @@ -200,19 +209,31 @@ def test_coreos_os_release(self): } self._test_outcome(desired_outcome) - def test_debian8_os_release(self): + def test_debian8_os_release(self) -> None: desired_outcome = { "id": "debian", "name": "Debian GNU/Linux", "pretty_name": "Debian GNU/Linux 8 (jessie)", "version": "8", "pretty_version": "8 (jessie)", - "best_version": "8", + "best_version": "8.2", "codename": "jessie", } self._test_outcome(desired_outcome) - def test_fedora19_os_release(self): + def test_debian10_os_release(self) -> None: + desired_outcome = { + "id": "debian", + "name": "Debian GNU/Linux", + "pretty_name": "Debian GNU/Linux 10 (buster)", + "version": "10", + "pretty_version": "10 (buster)", + "best_version": "10.11", + "codename": "buster", + } + self._test_outcome(desired_outcome) + + def test_fedora19_os_release(self) -> None: desired_outcome = { "id": "fedora", "name": "Fedora", @@ -224,7 +245,7 @@ def test_fedora19_os_release(self): } self._test_outcome(desired_outcome) - def test_fedora23_os_release(self): + def test_fedora23_os_release(self) -> None: desired_outcome = { "id": "fedora", "name": "Fedora", @@ -236,7 +257,7 @@ def test_fedora23_os_release(self): } self._test_outcome(desired_outcome) - def test_fedora30_os_release(self): + def test_fedora30_os_release(self) -> None: # 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 @@ -252,7 +273,15 @@ def test_fedora30_os_release(self): } self._test_outcome(desired_outcome) - def test_kvmibm1_os_release(self): + def test_guix_os_release(self) -> None: + desired_outcome = { + "id": "guix", + "name": "Guix System", + "pretty_name": "Guix System", + } + self._test_outcome(desired_outcome) + + def test_kvmibm1_os_release(self) -> None: desired_outcome = { "id": "kvmibm", "name": "KVM for IBM z Systems", @@ -265,7 +294,7 @@ def test_kvmibm1_os_release(self): } self._test_outcome(desired_outcome) - def test_linuxmint17_os_release(self): + def test_linuxmint17_os_release(self) -> None: # Note: LinuxMint 17 actually *does* have Ubuntu 14.04 data in its # os-release file. See discussion in GitHub issue #78. desired_outcome = { @@ -280,7 +309,7 @@ def test_linuxmint17_os_release(self): } self._test_outcome(desired_outcome) - def test_mageia5_os_release(self): + def test_mageia5_os_release(self) -> None: desired_outcome = { "id": "mageia", "name": "Mageia", @@ -292,12 +321,12 @@ def test_mageia5_os_release(self): } self._test_outcome(desired_outcome) - def test_manjaro1512_os_release(self): + def test_manjaro1512_os_release(self) -> None: self._test_outcome( {"id": "manjaro", "name": "Manjaro Linux", "pretty_name": "Manjaro Linux"} ) - def test_opensuse42_os_release(self): + def test_opensuse42_os_release(self) -> None: desired_outcome = { "id": "opensuse", "name": "openSUSE Leap", @@ -309,33 +338,45 @@ def test_opensuse42_os_release(self): } self._test_outcome(desired_outcome) - def test_raspbian7_os_release(self): + def test_opensuse15_os_release(self) -> None: + desired_outcome = { + "id": "opensuse", + "name": "openSUSE Leap", + "pretty_name": "openSUSE Leap 15.2", + "version": "15.2", + "pretty_version": "15.2", + "best_version": "15.2", + "like": "suse opensuse", + } + self._test_outcome(desired_outcome) + + def test_raspbian7_os_release(self) -> None: desired_outcome = { "id": "raspbian", "name": "Raspbian GNU/Linux", "pretty_name": "Raspbian GNU/Linux 7 (wheezy)", "version": "7", "pretty_version": "7 (wheezy)", - "best_version": "7", + "best_version": "7.1", "like": "debian", "codename": "wheezy", } self._test_outcome(desired_outcome) - def test_raspbian8_os_release(self): + def test_raspbian8_os_release(self) -> None: desired_outcome = { "id": "raspbian", "name": "Raspbian GNU/Linux", "pretty_name": "Raspbian GNU/Linux 8 (jessie)", "version": "8", "pretty_version": "8 (jessie)", - "best_version": "8", + "best_version": "8.0", "like": "debian", "codename": "jessie", } self._test_outcome(desired_outcome) - def test_rhel7_os_release(self): + def test_rhel7_os_release(self) -> None: desired_outcome = { "id": "rhel", "name": "Red Hat Enterprise Linux Server", @@ -348,7 +389,20 @@ def test_rhel7_os_release(self): } self._test_outcome(desired_outcome) - def test_slackware14_os_release(self): + def test_rocky_os_release(self) -> None: + desired_outcome = { + "id": "rocky", + "name": "Rocky Linux", + "pretty_name": "Rocky Linux 8.4 (Green Obsidian)", + "version": "8.4", + "pretty_version": "8.4 (Green Obsidian)", + "best_version": "8.4", + "like": "rhel centos fedora", + "codename": "Green Obsidian", + } + self._test_outcome(desired_outcome) + + def test_slackware14_os_release(self) -> None: desired_outcome = { "id": "slackware", "name": "Slackware", @@ -359,7 +413,7 @@ def test_slackware14_os_release(self): } self._test_outcome(desired_outcome) - def test_sles12_os_release(self): + def test_sles12_os_release(self) -> None: desired_outcome = { "id": "sles", "name": "SLES", @@ -370,7 +424,7 @@ def test_sles12_os_release(self): } self._test_outcome(desired_outcome) - def test_ubuntu14_os_release(self): + def test_ubuntu14_os_release(self) -> None: desired_outcome = { "id": "ubuntu", "name": "Ubuntu", @@ -383,7 +437,7 @@ def test_ubuntu14_os_release(self): } self._test_outcome(desired_outcome) - def test_ubuntu16_os_release(self): + def test_ubuntu16_os_release(self) -> None: desired_outcome = { "id": "ubuntu", "name": "Ubuntu", @@ -396,7 +450,7 @@ def test_ubuntu16_os_release(self): } self._test_outcome(desired_outcome) - def test_amazon2016_os_release(self): + def test_amazon2016_os_release(self) -> None: desired_outcome = { "id": "amzn", "name": "Amazon Linux AMI", @@ -408,7 +462,7 @@ def test_amazon2016_os_release(self): } self._test_outcome(desired_outcome) - def test_scientific7_os_release(self): + def test_scientific7_os_release(self) -> None: desired_outcome = { "id": "rhel", "name": "Scientific Linux", @@ -421,7 +475,7 @@ def test_scientific7_os_release(self): } self._test_outcome(desired_outcome) - def test_gentoo_os_release(self): + def test_gentoo_os_release(self) -> None: desired_outcome = { "id": "gentoo", "name": "Gentoo", @@ -429,7 +483,7 @@ def test_gentoo_os_release(self): } self._test_outcome(desired_outcome) - def test_openelec6_os_release(self): + def test_openelec6_os_release(self) -> None: desired_outcome = { "id": "openelec", "name": "OpenELEC", @@ -440,7 +494,7 @@ def test_openelec6_os_release(self): } self._test_outcome(desired_outcome) - def test_cloudlinux7_os_release(self): + def test_cloudlinux7_os_release(self) -> None: desired_outcome = { "id": "cloudlinux", "codename": "Yury Malyshev", @@ -461,31 +515,31 @@ class TestWithRootDir(TestOSRelease): on all OSes. """ - def setup_method(self, test_method): + def setup_method(self, test_method: FunctionType) -> None: dist = test_method.__name__.split("_")[1] root_dir = os.path.join(DISTROS_DIR, dist) self.distro = distro.LinuxDistribution( include_lsb=False, + include_uname=False, + include_oslevel=False, os_release_file="", distro_release_file="path-to-non-existing-file", - include_uname=False, root_dir=root_dir, ) @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) + def setup_method(self, test_method: FunctionType) -> None: + super().setup_method(test_method) dist = test_method.__name__.split("_")[1] self._setup_for_distro(os.path.join(DISTROS_DIR, dist)) self.distro = distro.LinuxDistribution( - include_lsb=True, os_release_file="path-to-non-existing-file", distro_release_file="path-to-non-existing-file", ) - def _test_outcome(self, outcome): + def _test_outcome(self, outcome: Dict[str, str]) -> None: assert self.distro.id() == outcome.get("id", "") assert self.distro.name() == outcome.get("name", "") assert self.distro.name(pretty=True) == outcome.get("pretty_name", "") @@ -495,7 +549,7 @@ def _test_outcome(self, outcome): assert self.distro.like() == outcome.get("like", "") assert self.distro.codename() == outcome.get("codename", "") - def test_linuxmint17_lsb_release(self): + def test_linuxmint17_lsb_release(self) -> None: desired_outcome = { "id": "linuxmint", "name": "LinuxMint", @@ -507,7 +561,7 @@ def test_linuxmint17_lsb_release(self): } self._test_outcome(desired_outcome) - def test_manjaro1512_lsb_release(self): + def test_manjaro1512_lsb_release(self) -> None: self._test_outcome( { "id": "manjarolinux", @@ -521,7 +575,7 @@ def test_manjaro1512_lsb_release(self): ) # @pytest.mark.xfail - # def test_openelec6_lsb_release(self): + # def test_openelec6_lsb_release(self) -> None: # # TODO: This should be fixed as part of #109 when dealing # # with distro inconsistencies # desired_outcome = { @@ -534,7 +588,7 @@ def test_manjaro1512_lsb_release(self): # } # self._test_outcome(desired_outcome) - def test_openbsd62_uname(self): + def test_openbsd62_uname(self) -> None: self._test_outcome( { "id": "openbsd", @@ -546,7 +600,7 @@ def test_openbsd62_uname(self): } ) - def test_netbsd711_uname(self): + def test_netbsd711_uname(self) -> None: self._test_outcome( { "id": "netbsd", @@ -558,7 +612,7 @@ def test_netbsd711_uname(self): } ) - def test_freebsd111_uname(self): + def test_freebsd111_uname(self) -> None: self._test_outcome( { "id": "freebsd", @@ -570,7 +624,7 @@ def test_freebsd111_uname(self): } ) - def test_midnightbsd12_uname(self): + def test_midnightbsd12_uname(self) -> None: self._test_outcome( { "id": "midnightbsd", @@ -582,11 +636,10 @@ def test_midnightbsd12_uname(self): } ) - def test_ubuntu14normal_lsb_release(self): + def test_ubuntu14normal_lsb_release(self) -> None: self._setup_for_distro(os.path.join(TESTDISTROS, "lsb", "ubuntu14_normal")) self.distro = distro.LinuxDistribution( - include_lsb=True, os_release_file="path-to-non-existing-file", distro_release_file="path-to-non-existing-file", ) @@ -602,11 +655,10 @@ def test_ubuntu14normal_lsb_release(self): } self._test_outcome(desired_outcome) - def test_ubuntu14nomodules_lsb_release(self): + def test_ubuntu14nomodules_lsb_release(self) -> None: self._setup_for_distro(os.path.join(TESTDISTROS, "lsb", "ubuntu14_nomodules")) self.distro = distro.LinuxDistribution( - include_lsb=True, os_release_file="path-to-non-existing-file", distro_release_file="path-to-non-existing-file", ) @@ -622,13 +674,12 @@ def test_ubuntu14nomodules_lsb_release(self): } self._test_outcome(desired_outcome) - def test_trailingblanks_lsb_release(self): + def test_trailingblanks_lsb_release(self) -> None: self._setup_for_distro( os.path.join(TESTDISTROS, "lsb", "ubuntu14_trailingblanks") ) self.distro = distro.LinuxDistribution( - include_lsb=True, os_release_file="path-to-non-existing-file", distro_release_file="path-to-non-existing-file", ) @@ -645,13 +696,10 @@ def test_trailingblanks_lsb_release(self): 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)) - ) + def test_lsb_release_error_level(self, errnum: str) -> None: + self._setup_for_distro(os.path.join(TESTDISTROS, "lsb", f"lsb_rc{errnum}")) lsb_release_info = distro.LinuxDistribution( - include_lsb=True, os_release_file="path-to-non-existing-file", distro_release_file="path-to-non-existing-file", )._lsb_release_info @@ -661,7 +709,7 @@ def test_lsb_release_error_level(self, errnum): @pytest.mark.skipif(not IS_LINUX, reason="Irrelevant on non-linux") class TestSpecialRelease(DistroTestCase): - def _test_outcome(self, outcome): + def _test_outcome(self, outcome: Dict[str, str]) -> None: assert self.distro.id() == outcome.get("id", "") assert self.distro.name() == outcome.get("name", "") assert self.distro.name(pretty=True) == outcome.get("pretty_name", "") @@ -674,7 +722,7 @@ def _test_outcome(self, outcome): assert self.distro.minor_version() == outcome.get("minor_version", "") assert self.distro.build_number() == outcome.get("build_number", "") - def test_empty_release(self): + def test_empty_release(self) -> None: distro_release = os.path.join(SPECIAL, "empty-release") self.distro = distro.LinuxDistribution( @@ -686,7 +734,16 @@ def test_empty_release(self): desired_outcome = {"id": "empty"} self._test_outcome(desired_outcome) - def test_unknowndistro_release(self): + def test_dontincludeuname(self) -> None: + self._setup_for_distro(os.path.join(TESTDISTROS, "distro", "dontincludeuname")) + + self.distro = distro.LinuxDistribution(include_uname=False) + + assert self.distro.uname_attr("id") == "" + assert self.distro.uname_attr("name") == "" + assert self.distro.uname_attr("release") == "" + + def test_unknowndistro_release(self) -> None: self._setup_for_distro(os.path.join(TESTDISTROS, "distro", "unknowndistro")) self.distro = distro.LinuxDistribution() @@ -704,7 +761,7 @@ def test_unknowndistro_release(self): } self._test_outcome(desired_outcome) - def test_bad_uname(self): + def test_bad_uname(self) -> None: self._setup_for_distro(os.path.join(TESTDISTROS, "distro", "baduname")) self.distro = distro.LinuxDistribution() @@ -712,7 +769,15 @@ def test_bad_uname(self): assert self.distro.uname_attr("name") == "" assert self.distro.uname_attr("release") == "" - def test_usrlibosreleaseonly(self): + def test_empty_uname(self) -> None: + self._setup_for_distro(os.path.join(TESTDISTROS, "distro", "emptyuname")) + self.distro = distro.LinuxDistribution() + + assert self.distro.uname_attr("id") == "" + assert self.distro.uname_attr("name") == "" + assert self.distro.uname_attr("release") == "" + + def test_usrlibosreleaseonly(self) -> None: self._setup_for_distro( os.path.join(TESTDISTROS, "distro", "usrlibosreleaseonly") ) @@ -730,18 +795,18 @@ def test_usrlibosreleaseonly(self): class TestDistroRelease: def _test_outcome( self, - outcome, - distro_name="", - version="", - release_file_id="", - release_file_suffix="release", - ): + outcome: Dict[str, str], + distro_name: str = "", + version: str = "", + release_file_id: str = "", + release_file_suffix: str = "release", + ) -> None: 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), + f"{release_file_id}-{release_file_suffix}", ) self.distro = distro.LinuxDistribution( include_lsb=False, @@ -761,11 +826,11 @@ def _test_outcome( assert self.distro.minor_version() == outcome.get("minor_version", "") assert self.distro.build_number() == outcome.get("build_number", "") - def test_arch_dist_release(self): + def test_arch_dist_release(self) -> None: desired_outcome = {"id": "arch"} self._test_outcome(desired_outcome, "arch") - def test_centos5_dist_release(self): + def test_centos5_dist_release(self) -> None: desired_outcome = { "id": "centos", "name": "CentOS", @@ -779,7 +844,7 @@ def test_centos5_dist_release(self): } self._test_outcome(desired_outcome, "centos", "5") - def test_centos7_dist_release(self): + def test_centos7_dist_release(self) -> None: desired_outcome = { "id": "centos", "name": "CentOS Linux", @@ -794,7 +859,7 @@ def test_centos7_dist_release(self): } self._test_outcome(desired_outcome, "centos", "7") - def test_fedora19_dist_release(self): + def test_fedora19_dist_release(self) -> None: desired_outcome = { "id": "fedora", "name": "Fedora", @@ -807,7 +872,7 @@ def test_fedora19_dist_release(self): } self._test_outcome(desired_outcome, "fedora", "19") - def test_fedora23_dist_release(self): + def test_fedora23_dist_release(self) -> None: desired_outcome = { "id": "fedora", "name": "Fedora", @@ -820,7 +885,7 @@ def test_fedora23_dist_release(self): } self._test_outcome(desired_outcome, "fedora", "23") - def test_fedora30_dist_release(self): + def test_fedora30_dist_release(self) -> None: desired_outcome = { "id": "fedora", "name": "Fedora", @@ -833,7 +898,7 @@ def test_fedora30_dist_release(self): } self._test_outcome(desired_outcome, "fedora", "30") - def test_gentoo_dist_release(self): + def test_gentoo_dist_release(self) -> None: desired_outcome = { "id": "gentoo", "name": "Gentoo Base System", @@ -846,7 +911,7 @@ def test_gentoo_dist_release(self): } self._test_outcome(desired_outcome, "gentoo") - def test_kvmibm1_dist_release(self): + def test_kvmibm1_dist_release(self) -> None: desired_outcome = { "id": "base", "name": "KVM for IBM z Systems", @@ -861,7 +926,7 @@ def test_kvmibm1_dist_release(self): } self._test_outcome(desired_outcome, "kvmibm", "1", "base") - def test_mageia5_dist_release(self): + def test_mageia5_dist_release(self) -> None: desired_outcome = { "id": "mageia", "name": "Mageia", @@ -874,7 +939,7 @@ def test_mageia5_dist_release(self): } self._test_outcome(desired_outcome, "mageia", "5") - def test_manjaro1512_dist_release(self): + def test_manjaro1512_dist_release(self) -> None: self._test_outcome( { "id": "manjaro", @@ -887,7 +952,7 @@ def test_manjaro1512_dist_release(self): "1512", ) - def test_opensuse42_dist_release(self): + def test_opensuse42_dist_release(self) -> None: desired_outcome = { "id": "suse", "name": "openSUSE", @@ -901,7 +966,7 @@ def test_opensuse42_dist_release(self): } self._test_outcome(desired_outcome, "opensuse", "42", "SuSE") - def test_oracle7_dist_release(self): + def test_oracle7_dist_release(self) -> None: desired_outcome = { "id": "oracle", "name": "Oracle Linux Server", @@ -914,7 +979,7 @@ def test_oracle7_dist_release(self): } self._test_outcome(desired_outcome, "oracle", "7") - def test_rhel6_dist_release(self): + def test_rhel6_dist_release(self) -> None: desired_outcome = { "id": "rhel", "name": "Red Hat Enterprise Linux Server", @@ -928,7 +993,7 @@ def test_rhel6_dist_release(self): } self._test_outcome(desired_outcome, "rhel", "6", "redhat") - def test_rhel7_dist_release(self): + def test_rhel7_dist_release(self) -> None: desired_outcome = { "id": "rhel", "name": "Red Hat Enterprise Linux Server", @@ -942,7 +1007,7 @@ def test_rhel7_dist_release(self): } self._test_outcome(desired_outcome, "rhel", "7", "redhat") - def test_slackware14_dist_release(self): + def test_slackware14_dist_release(self) -> None: desired_outcome = { "id": "slackware", "name": "Slackware", @@ -957,7 +1022,7 @@ def test_slackware14_dist_release(self): desired_outcome, "slackware", "14", release_file_suffix="version" ) - def test_sles12_dist_release(self): + def test_sles12_dist_release(self) -> None: desired_outcome = { "id": "suse", "name": "SUSE Linux Enterprise Server", @@ -970,7 +1035,7 @@ def test_sles12_dist_release(self): } self._test_outcome(desired_outcome, "sles", "12", "SuSE") - def test_cloudlinux5_dist_release(self): + def test_cloudlinux5_dist_release(self) -> None: # Uses redhat-release only to get information. # The id of 'rhel' can only be fixed with issue #109. desired_outcome = { @@ -986,7 +1051,7 @@ def test_cloudlinux5_dist_release(self): } self._test_outcome(desired_outcome, "cloudlinux", "5", "redhat") - def test_cloudlinux6_dist_release(self): + def test_cloudlinux6_dist_release(self) -> None: # Same as above, only has redhat-release. desired_outcome = { "id": "cloudlinux", @@ -1001,7 +1066,7 @@ def test_cloudlinux6_dist_release(self): } self._test_outcome(desired_outcome, "cloudlinux", "6", "redhat") - def test_cloudlinux7_dist_release(self): + def test_cloudlinux7_dist_release(self) -> None: desired_outcome = { "id": "cloudlinux", "codename": "Yury Malyshev", @@ -1032,7 +1097,6 @@ class TestOverall(DistroTestCase): 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 @@ -1042,13 +1106,13 @@ class TestOverall(DistroTestCase): * `xenserver` - XenServer """ - def setup_method(self, test_method): - super(TestOverall, self).setup_method(test_method) + def setup_method(self, test_method: FunctionType) -> None: + super().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): + def _test_outcome(self, outcome: Dict[str, str]) -> None: assert self.distro.id() == outcome.get("id", "") assert self.distro.name() == outcome.get("name", "") assert self.distro.name(pretty=True) == outcome.get("pretty_name", "") @@ -1061,13 +1125,15 @@ def _test_outcome(self, outcome): 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): + def _test_non_existing_release_file(self) -> None: # 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): + def _test_release_file_info( + self, filename: str, outcome: Dict[str, str] + ) -> Dict[str, str]: # 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() @@ -1075,7 +1141,7 @@ def _test_release_file_info(self, filename, outcome): assert distro_info[key] == value return distro_info - def test_arch_release(self): + def test_arch_release(self) -> None: desired_outcome = { "id": "arch", "name": "Arch Linux", @@ -1088,7 +1154,21 @@ def test_arch_release(self): # considered a valid distro release file: self._test_non_existing_release_file() - def test_centos5_release(self): + def test_aix72_release(self) -> None: + desired_outcome = { + "id": "aix", + "name": "AIX", + "pretty_name": "AIX 7.2.0.0", + "version": "7.2.0.0", + "pretty_version": "7.2.0.0", + "best_version": "7.2.0.0", + "major_version": "7", + "minor_version": "2", + "build_number": "0", + } + self._test_outcome(desired_outcome) + + def test_centos5_release(self) -> None: desired_outcome = { "id": "centos", "name": "CentOS", @@ -1110,7 +1190,7 @@ def test_centos5_release(self): } self._test_release_file_info("centos-release", desired_info) - def test_centos7_release(self): + def test_centos7_release(self) -> None: desired_outcome = { "id": "centos", "name": "CentOS Linux", @@ -1132,7 +1212,7 @@ def test_centos7_release(self): } self._test_release_file_info("centos-release", desired_info) - def test_coreos_release(self): + def test_coreos_release(self) -> None: desired_outcome = { "id": "coreos", "name": "CoreOS", @@ -1147,7 +1227,7 @@ def test_coreos_release(self): self._test_outcome(desired_outcome) self._test_non_existing_release_file() - def test_debian8_release(self): + def test_debian8_release(self) -> None: desired_outcome = { "id": "debian", "name": "Debian GNU/Linux", @@ -1161,7 +1241,21 @@ def test_debian8_release(self): self._test_outcome(desired_outcome) self._test_non_existing_release_file() - def test_exherbo_release(self): + def test_debian10_release(self) -> None: + desired_outcome = { + "id": "debian", + "name": "Debian GNU/Linux", + "pretty_name": "Debian GNU/Linux 10 (buster)", + "version": "10", + "pretty_version": "10 (buster)", + "best_version": "10.11", + "codename": "buster", + "major_version": "10", + } + self._test_outcome(desired_outcome) + self._test_non_existing_release_file() + + def test_exherbo_release(self) -> None: desired_outcome = { "id": "exherbo", "name": "Exherbo", @@ -1169,7 +1263,7 @@ def test_exherbo_release(self): } self._test_outcome(desired_outcome) - def test_fedora19_release(self): + def test_fedora19_release(self) -> None: desired_outcome = { "id": "fedora", "name": "Fedora", @@ -1190,7 +1284,7 @@ def test_fedora19_release(self): } self._test_release_file_info("fedora-release", desired_info) - def test_fedora23_release(self): + def test_fedora23_release(self) -> None: desired_outcome = { "id": "fedora", "name": "Fedora", @@ -1211,7 +1305,7 @@ def test_fedora23_release(self): } self._test_release_file_info("fedora-release", desired_info) - def test_fedora30_release(self): + def test_fedora30_release(self) -> None: desired_outcome = { "id": "fedora", "name": "Fedora", @@ -1232,7 +1326,7 @@ def test_fedora30_release(self): } self._test_release_file_info("fedora-release", desired_info) - def test_kvmibm1_release(self): + def test_kvmibm1_release(self) -> None: desired_outcome = { "id": "kvmibm", "name": "KVM for IBM z Systems", @@ -1256,7 +1350,7 @@ def test_kvmibm1_release(self): } self._test_release_file_info("base-release", desired_info) - def test_linuxmint17_release(self): + def test_linuxmint17_release(self) -> None: desired_outcome = { "id": "ubuntu", "name": "Ubuntu", @@ -1272,7 +1366,7 @@ def test_linuxmint17_release(self): self._test_outcome(desired_outcome) self._test_non_existing_release_file() - def test_mageia5_release(self): + def test_mageia5_release(self) -> None: desired_outcome = { "id": "mageia", "name": "Mageia", @@ -1295,7 +1389,7 @@ def test_mageia5_release(self): } self._test_release_file_info("mageia-release", desired_info) - def test_manjaro1512_release(self): + def test_manjaro1512_release(self) -> None: self._test_outcome( { "id": "manjaro", @@ -1314,7 +1408,7 @@ def test_manjaro1512_release(self): "manjaro-release", {"id": "manjaro", "name": "Manjaro Linux"} ) - def test_opensuse42_release(self): + def test_opensuse42_release(self) -> None: desired_outcome = { "id": "opensuse", "name": "openSUSE Leap", @@ -1337,7 +1431,21 @@ def test_opensuse42_release(self): } self._test_release_file_info("SuSE-release", desired_info) - def test_oracle7_release(self): + def test_opensuse15_release(self) -> None: + desired_outcome = { + "id": "opensuse", + "name": "openSUSE Leap", + "pretty_name": "openSUSE Leap 15.2", + "version": "15.2", + "pretty_version": "15.2", + "best_version": "15.2", + "like": "suse opensuse", + "major_version": "15", + "minor_version": "2", + } + self._test_outcome(desired_outcome) + + def test_oracle7_release(self) -> None: desired_outcome = { "id": "oracle", "name": "Oracle Linux Server", @@ -1358,14 +1466,14 @@ def test_oracle7_release(self): distro_info = self._test_release_file_info("oracle-release", desired_info) assert "codename" not in distro_info - def test_raspbian7_release(self): + def test_raspbian7_release(self) -> None: desired_outcome = { "id": "raspbian", "name": "Raspbian GNU/Linux", "pretty_name": "Raspbian GNU/Linux 7 (wheezy)", "version": "7", "pretty_version": "7 (wheezy)", - "best_version": "7", + "best_version": "7.1", "like": "debian", "codename": "wheezy", "major_version": "7", @@ -1373,14 +1481,14 @@ def test_raspbian7_release(self): self._test_outcome(desired_outcome) self._test_non_existing_release_file() - def test_raspbian8_release(self): + def test_raspbian8_release(self) -> None: desired_outcome = { "id": "raspbian", "name": "Raspbian GNU/Linux", "pretty_name": "Raspbian GNU/Linux 8 (jessie)", "version": "8", "pretty_version": "8 (jessie)", - "best_version": "8", + "best_version": "8.0", "like": "debian", "codename": "jessie", "major_version": "8", @@ -1388,7 +1496,7 @@ def test_raspbian8_release(self): self._test_outcome(desired_outcome) self._test_non_existing_release_file() - def test_rhel5_release(self): + def test_rhel5_release(self) -> None: desired_outcome = { "id": "rhel", "name": "Red Hat Enterprise Linux Server", @@ -1410,7 +1518,7 @@ def test_rhel5_release(self): } self._test_release_file_info("redhat-release", desired_info) - def test_rhel6_release(self): + def test_rhel6_release(self) -> None: desired_outcome = { "id": "rhel", "name": "Red Hat Enterprise Linux Server", @@ -1432,7 +1540,7 @@ def test_rhel6_release(self): } self._test_release_file_info("redhat-release", desired_info) - def test_rhel7_release(self): + def test_rhel7_release(self) -> None: desired_outcome = { "id": "rhel", "name": "Red Hat Enterprise Linux Server", @@ -1455,7 +1563,30 @@ def test_rhel7_release(self): } self._test_release_file_info("redhat-release", desired_info) - def test_slackware14_release(self): + def test_rocky_release(self) -> None: + desired_outcome = { + "id": "rocky", + "name": "Rocky Linux", + "pretty_name": "Rocky Linux 8.4 (Green Obsidian)", + "version": "8.4", + "pretty_version": "8.4 (Green Obsidian)", + "best_version": "8.4", + "like": "rhel centos fedora", + "codename": "Green Obsidian", + "major_version": "8", + "minor_version": "4", + } + self._test_outcome(desired_outcome) + + desired_info = { + "id": "centos", + "name": "Rocky Linux", + "version_id": "8.4", + "codename": "Green Obsidian", + } + self._test_release_file_info("centos-release", desired_info) + + def test_slackware14_release(self) -> None: desired_outcome = { "id": "slackware", "name": "Slackware", @@ -1472,7 +1603,7 @@ def test_slackware14_release(self): distro_info = self._test_release_file_info("slackware-version", desired_info) assert "codename" not in distro_info - def test_sles12_release(self): + def test_sles12_release(self) -> None: desired_outcome = { "id": "sles", "name": "SLES", @@ -1494,7 +1625,7 @@ def test_sles12_release(self): } self._test_release_file_info("SuSE-release", desired_info) - def test_ubuntu14_release(self): + def test_ubuntu14_release(self) -> None: desired_outcome = { "id": "ubuntu", "name": "Ubuntu", @@ -1514,7 +1645,7 @@ def test_ubuntu14_release(self): # release file: self._test_non_existing_release_file() - def test_ubuntu16_release(self): + def test_ubuntu16_release(self) -> None: desired_outcome = { "id": "ubuntu", "name": "Ubuntu", @@ -1534,7 +1665,7 @@ def test_ubuntu16_release(self): # release file: self._test_non_existing_release_file() - def test_amazon2016_release(self): + def test_amazon2016_release(self) -> None: desired_outcome = { "id": "amzn", "name": "Amazon Linux AMI", @@ -1548,13 +1679,12 @@ def test_amazon2016_release(self): } self._test_outcome(desired_outcome) - def test_amazon2014_release(self): + def test_amazon2014_release(self) -> None: # Amazon Linux 2014 only contains a system-release file. # distro doesn't currently handle it. - desired_outcome = {} - self._test_outcome(desired_outcome) + self._test_outcome({}) - def test_scientific6_release(self): + def test_scientific6_release(self) -> None: desired_outcome = { "id": "rhel", "name": "Scientific Linux", @@ -1576,7 +1706,7 @@ def test_scientific6_release(self): } self._test_release_file_info("redhat-release", desired_info) - def test_scientific7_release(self): + def test_scientific7_release(self) -> None: desired_outcome = { "id": "rhel", "name": "Scientific Linux", @@ -1599,7 +1729,7 @@ def test_scientific7_release(self): } self._test_release_file_info("redhat-release", desired_info) - def test_gentoo_release(self): + def test_gentoo_release(self) -> None: desired_outcome = { "id": "gentoo", "name": "Gentoo", @@ -1619,7 +1749,7 @@ def test_gentoo_release(self): } self._test_release_file_info("gentoo-release", desired_info) - def test_openelec6_release(self): + def test_openelec6_release(self) -> None: desired_outcome = { "id": "openelec", "name": "OpenELEC", @@ -1632,7 +1762,7 @@ def test_openelec6_release(self): } self._test_outcome(desired_outcome) - def test_mandriva2011_release(self): + def test_mandriva2011_release(self) -> None: desired_outcome = { "id": "mandrivalinux", "name": "MandrivaLinux", @@ -1653,7 +1783,7 @@ def test_mandriva2011_release(self): } self._test_release_file_info("mandrake-release", desired_info) - def test_cloudlinux5_release(self): + def test_cloudlinux5_release(self) -> None: # Uses redhat-release only to get information. # The id of 'rhel' can only be fixed with issue #109. desired_outcome = { @@ -1669,7 +1799,7 @@ def test_cloudlinux5_release(self): } self._test_outcome(desired_outcome) - def test_cloudlinux6_release(self): + def test_cloudlinux6_release(self) -> None: # Same as above, only has redhat-release. desired_outcome = { "id": "cloudlinux", @@ -1684,7 +1814,7 @@ def test_cloudlinux6_release(self): } self._test_outcome(desired_outcome) - def test_cloudlinux7_release(self): + def test_cloudlinux7_release(self) -> None: desired_outcome = { "id": "cloudlinux", "codename": "Yury Malyshev", @@ -1700,7 +1830,7 @@ def test_cloudlinux7_release(self): self._test_outcome(desired_outcome) -def _bad_os_listdir(path="."): +def _bad_os_listdir(path: str = ".") -> NoReturn: """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.""" @@ -1709,13 +1839,15 @@ def _bad_os_listdir(path="."): @pytest.mark.skipif(not IS_LINUX, reason="Irrelevant on non-linux") class TestOverallWithEtcNotReadable(TestOverall): - def setup_method(self, test_method): + def setup_method(self, test_method: FunctionType) -> None: self._old_listdir = os.listdir - os.listdir = _bad_os_listdir - super(TestOverallWithEtcNotReadable, self).setup_method(test_method) + # Incompatible types in assignment (expression has type + # "Callable[[str], NoReturn]", variable has type overloaded function) + os.listdir = _bad_os_listdir # type: ignore[assignment] + super().setup_method(test_method) - def teardown_method(self, test_method): - super(TestOverallWithEtcNotReadable, self).teardown_method(test_method) + def teardown_method(self, test_method: FunctionType) -> None: + super().teardown_method(test_method) if os.listdir is _bad_os_listdir: os.listdir = self._old_listdir @@ -1727,7 +1859,7 @@ class TestGetAttr(DistroTestCase): distros in `DISTROS`. """ - def _test_attr(self, info_method, attr_method): + def _test_attr(self, info_method: str, attr_method: str) -> None: for dist in DISTROS: self._setup_for_distro(os.path.join(DISTROS_DIR, dist)) _distro = distro.LinuxDistribution() @@ -1736,27 +1868,30 @@ def _test_attr(self, info_method, attr_method): try: assert info[key] == getattr(_distro, attr_method)(key) except AssertionError: - print("distro: {0}, key: {1}".format(dist, key)) + print(f"distro: {dist}, key: {key}") - def test_os_release_attr(self): + def test_os_release_attr(self) -> None: self._test_attr("os_release_info", "os_release_attr") - def test_lsb_release_attr(self): + def test_lsb_release_attr(self) -> None: self._test_attr("lsb_release_info", "lsb_release_attr") - def test_distro_release_attr(self): + def test_distro_release_attr(self) -> None: 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) + def setup_method(self, test_method: FunctionType) -> None: + super().setup_method(test_method) self.ubuntu14_os_release = os.path.join( DISTROS_DIR, "ubuntu14", "etc", "os-release" ) + self.fedora30_os_release = os.path.join( + DISTROS_DIR, "fedora30", "etc", "os-release" + ) - def test_info(self): + def test_info(self) -> None: _distro = distro.LinuxDistribution( include_lsb=False, os_release_file=self.ubuntu14_os_release, @@ -1774,7 +1909,7 @@ def test_info(self): info = _distro.info() assert info == desired_info - desired_info_diff = {"version": "14.04 (Trusty Tahr)"} + desired_info_diff: Dict[str, Any] = {"version": "14.04 (Trusty Tahr)"} desired_info.update(desired_info_diff) info = _distro.info(pretty=True) assert info == desired_info @@ -1792,8 +1927,8 @@ def test_info(self): info = _distro.info(pretty=True, best=True) assert info == desired_info - def test_none(self): - def _test_none(info): + def test_none(self) -> None: + def _test_none(info: distro.InfoDict) -> None: assert info["id"] == "" assert info["version"] == "" assert info["like"] == "" @@ -1820,26 +1955,34 @@ def _test_none(info): info = _distro.info(pretty=True, best=True) _test_none(info) - def test_linux_distribution(self): + def test_linux_distribution(self) -> None: _distro = distro.LinuxDistribution( include_lsb=False, os_release_file=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( + include_lsb=False, os_release_file=self.fedora30_os_release + ) + i = _distro.linux_distribution() + assert i == ("Fedora", "30", "Thirty") + + def test_linux_distribution_full_false(self) -> None: _distro = distro.LinuxDistribution( include_lsb=False, os_release_file=self.ubuntu14_os_release ) i = _distro.linux_distribution(full_distribution_name=False) assert i == ("ubuntu", "14.04", "Trusty Tahr") - def test_all(self): + def test_all(self) -> None: """Test info() by comparing its results with the results of specific consolidated accessor functions. """ - def _test_all(info, best=False, pretty=False): + def _test_all( + info: distro.InfoDict, best: bool = False, pretty: bool = False + ) -> None: assert info["id"] == _distro.id() assert info["version"] == _distro.version(pretty=pretty, best=best) assert info["version_parts"]["major"] == _distro.major_version(best=best) @@ -1874,157 +2017,152 @@ def _test_all(info, best=False, pretty=False): class TestOSReleaseParsing: """Test the parsing of os-release files.""" - def setup_method(self, test_method): - self.distro = distro.LinuxDistribution( - include_lsb=False, os_release_file=None, distro_release_file=None - ) - - self.distro.debug = True + def setup_method(self, test_method: FunctionType) -> None: + self.distro = distro.LinuxDistribution(include_lsb=False) - def _get_props(self, input): - return self.distro._parse_os_release_content(StringIO(input)) + def _get_props(self, input: str) -> Dict[str, str]: + return self.distro._parse_os_release_content(io.StringIO(input)) - def _test_zero_length_props(self, input): + def _test_zero_length_props(self, input: str) -> None: props = self._get_props(input) assert len(props) == 0 - def _test_empty_value(self, input): + def _test_empty_value(self, input: str) -> None: props = self._get_props(input) assert props.get("key", None) == "" - def _test_parsed_value(self, input): + def _test_parsed_value(self, input: str) -> None: props = self._get_props(input) assert props.get("key", None) == "value" - def test_kv_01_empty_file(self): + def test_kv_01_empty_file(self) -> None: self._test_zero_length_props("") - def test_kv_02_empty_line(self): + def test_kv_02_empty_line(self) -> None: self._test_zero_length_props("\n") - def test_kv_03_empty_line_with_crlf(self): + def test_kv_03_empty_line_with_crlf(self) -> None: self._test_zero_length_props("\r\n") - def test_kv_04_empty_line_with_just_cr(self): + def test_kv_04_empty_line_with_just_cr(self) -> None: self._test_zero_length_props("\r") - def test_kv_05_comment(self): + def test_kv_05_comment(self) -> None: self._test_zero_length_props("# KEY=value\n") - def test_kv_06_empty_value(self): + def test_kv_06_empty_value(self) -> None: self._test_empty_value("KEY=\n") - def test_kv_07_empty_value_single_quoted(self): + def test_kv_07_empty_value_single_quoted(self) -> None: self._test_empty_value("KEY=''\n") - def test_kv_08_empty_value_double_quoted(self): + def test_kv_08_empty_value_double_quoted(self) -> None: self._test_empty_value('KEY=""\n') - def test_kv_09_word(self): + def test_kv_09_word(self) -> None: self._test_parsed_value("KEY=value\n") - def test_kv_10_word_no_newline(self): + def test_kv_10_word_no_newline(self) -> None: self._test_parsed_value("KEY=value") - def test_kv_11_word_with_crlf(self): + def test_kv_11_word_with_crlf(self) -> None: self._test_parsed_value("KEY=value\r\n") - def test_kv_12_word_with_just_cr(self): + def test_kv_12_word_with_just_cr(self) -> None: self._test_parsed_value("KEY=value\r") - def test_kv_13_word_with_multi_blanks(self): + def test_kv_13_word_with_multi_blanks(self) -> None: 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): + def test_kv_14_unquoted_words(self) -> None: self._test_parsed_value("KEY=value cmd\n") - def test_kv_15_double_quoted_words(self): + def test_kv_15_double_quoted_words(self) -> None: 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): + def test_kv_16_double_quoted_words_with_multi_blanks(self) -> None: 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): + def test_kv_17_double_quoted_word_with_single_quote(self) -> None: 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): + def test_kv_18_double_quoted_word_with_double_quote(self) -> None: 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): + def test_kv_19_single_quoted_words(self) -> None: 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): + def test_kv_20_single_quoted_words_with_multi_blanks(self) -> None: 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): + def test_kv_21_single_quoted_word_with_double_quote(self) -> None: 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): + def test_kv_22_quoted_unicode_wordchar(self) -> None: # "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)" + props = self._get_props('KEY="wordchar: \u00CA (E accent grave)"\n') + assert props.get("key", None) == "wordchar: \u00CA (E accent grave)" - def test_kv_23_quoted_unicode_non_wordchar(self): + def test_kv_23_quoted_unicode_non_wordchar(self) -> None: # "non-wordchar" means it is not in the shlex.wordchars variable. props = self._get_props( - u'KEY="non-wordchar: \u00A1 (inverted exclamation mark)"\n' + 'KEY="non-wordchar: \u00A1 (inverted exclamation mark)"\n' ) assert ( - props.get("key", None) - == u"non-wordchar: \u00A1 (inverted exclamation mark)" + props.get("key", None) == "non-wordchar: \u00A1 (inverted exclamation mark)" ) - def test_kv_24_double_quoted_entire_single_quoted_word(self): + def test_kv_24_double_quoted_entire_single_quoted_word(self) -> None: props = self._get_props("KEY=\"'value'\"\n") assert props.get("key", None) == "'value'" - def test_kv_25_single_quoted_entire_double_quoted_word(self): + def test_kv_25_single_quoted_entire_double_quoted_word(self) -> None: props = self._get_props("KEY='\"value\"'\n") assert props.get("key", None) == '"value"' - def test_kv_26_double_quoted_multiline(self): + def test_kv_26_double_quoted_multiline(self) -> None: props = self.distro._parse_os_release_content( - StringIO('KEY="a multi\n' 'line value"\n') + io.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): + def test_kv_27_double_quoted_multiline_2(self) -> None: props = self._get_props("KEY=' a simple value '\n") props = self.distro._parse_os_release_content( - StringIO('KEY="a multi\n' 'line=value"\n') + io.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): + def test_kv_28_double_quoted_word_with_equal(self) -> None: 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): + def test_kv_29_single_quoted_word_with_equal(self) -> None: props = self._get_props("KEY='var=value'\n") assert props.get("key", None) == "var=value" - def test_kx_01(self): + def test_kx_01(self) -> None: props = self.distro._parse_os_release_content( - StringIO("KEY1=value1\n" 'KEY2="value 2"\n') + io.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): + def test_kx_02(self) -> None: props = self.distro._parse_os_release_content( - StringIO("# KEY1=value1\n" 'KEY2="value 2"\n') + io.StringIO("# KEY1=value1\n" 'KEY2="value 2"\n') ) assert props.get("key1", None) is None assert props.get("key2", None) == "value 2" @@ -2036,10 +2174,10 @@ class TestGlobal: arguments. """ - def setup_method(self, test_method): + def setup_method(self, test_method: FunctionType) -> None: pass - def test_global(self): + def test_global(self) -> None: # 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 @@ -2048,7 +2186,9 @@ def test_global(self): # compare the result of the global functions with the result # of the methods on the global LinuxDistribution object. - def _test_consistency(function, kwargs=None): + def _test_consistency( + function: str, kwargs: Optional[Dict[str, Any]] = None + ) -> None: kwargs = kwargs or {} method_result = getattr(MODULE_DISTRO, function)(**kwargs) function_result = getattr(distro, function)(**kwargs) @@ -2126,28 +2266,12 @@ def _test_consistency(function, kwargs=None): class TestRepr: """Test the __repr__() method.""" - def test_repr(self): + def test_repr(self) -> None: # 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(): - if attr in ("root_dir", "etc_dir", "usr_lib_dir"): + if attr in ("root_dir", "etc_dir", "usr_lib_dir", "_debian_version"): continue - assert attr + "=" in repr_str - - -@pytest.mark.skipif(not IS_LINUX, reason="Irrelevant on non-linux") -class TestToStr: - """Test the _to_str() method.""" - - def test_to_str(self): - ret = distro.LinuxDistribution._to_str(b"bytes") - assert isinstance(ret, str) - assert ret == "bytes" - - ret = distro.LinuxDistribution._to_str(u"bytes") - assert isinstance(ret, str) - - ret = distro.LinuxDistribution._to_str("bytes") - assert isinstance(ret, str) + assert f"{attr}=" in repr_str diff --git a/setup.py b/tox.ini similarity index 59% rename from setup.py rename to tox.ini index 4ab6be2..cc9dfc5 100644 --- a/setup.py +++ b/tox.ini @@ -1,4 +1,4 @@ -# Copyright 2015-2020 Nir Cohen +# 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. @@ -12,6 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -from setuptools import setup +[tox] +minversion = 1.9 +envlist = lint, py{36,37,38,39,310,py3} +isolated_build = true +skip_missing_interpreters = true -setup() +[testenv] +deps = + pytest + pytest-cov +commands = pytest --cov-report term-missing --cov distro {posargs} + +[testenv:lint] +deps = pre-commit +commands = pre-commit run --all-files --show-diff-on-failure +skip_install = true