From 65a8aaac16657164544341bd710c4edc5deffc91 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 23 Jan 2023 17:08:47 -0600 Subject: [PATCH 001/112] Drop old Pythons, add new pythons - Remove 2.7, 3.4, and 3.5 - Remove Jython - Remove IronPython - Add 3.10, 3.11, and 3.12 - Start update of dev tooling --- .coveragerc | 6 - .github/workflows/ci.yml | 20 +- CHANGELOG.md | 21 +- MANIFEST.in | 2 +- README.md | 15 +- docs/TODO.md | 11 +- docs/conf.py | 1 - getmac/__main__.py | 3 +- getmac/getmac.py | 104 ++------ getmac/shutilwhich.py | 67 ----- packaging/rpm/build-rpm-python2.sh | 18 -- packaging/rpm/man/getmac2.1 | 390 ----------------------------- packaging/rpm/python2-getmac.spec | 187 -------------- scripts/centos-provision.sh | 1 - scripts/openbsd-provision.sh | 3 +- scripts/ubuntu-provision.sh | 3 +- setup.py | 30 ++- tests/conftest.py | 1 - tests/test-requirements.txt | 15 +- tests/test_cli.py | 8 - tests/test_getmac.py | 8 +- tests/test_methods.py | 2 - tox.ini | 25 +- 23 files changed, 106 insertions(+), 835 deletions(-) delete mode 100644 .coveragerc delete mode 100644 getmac/shutilwhich.py delete mode 100644 packaging/rpm/build-rpm-python2.sh delete mode 100644 packaging/rpm/man/getmac2.1 delete mode 100644 packaging/rpm/python2-getmac.spec diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 0c32bb3..0000000 --- a/.coveragerc +++ /dev/null @@ -1,6 +0,0 @@ -# Temporary hack until we remove shutilwhich with removal of Python 2 -# TODO: move relative_files into tox.ini -[run] -relative_files = True -omit = - getmac/shutilwhich.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f0b18b..2ce8b0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,13 +30,13 @@ jobs: # OS images: https://github.com/actions/runner-images matrix: python-version: - - "2.7" - - "3.5" - "3.6" - "3.7" - "3.8" - "3.9" - - "pypy-2.7" + - "3.10" + - "3.11" + - "3.12" - "pypy-3.6" - "pypy-3.7" - "pypy-3.8" @@ -49,24 +49,18 @@ jobs: - windows-latest - macos-latest exclude: - - platform: ubuntu-20.04 - architecture: x86 + # - platform: ubuntu-20.04 + # architecture: x86 - platform: macos-latest architecture: x86 - - platform: ubuntu-20.04 - python-version: "3.4" - - platform: macos-latest - python-version: "3.4" - - platform: macos-latest - python-version: "pypy-2.7" - platform: macos-latest python-version: "pypy-3.6" - platform: macos-latest python-version: "pypy-3.7" - platform: macos-latest python-version: "pypy-3.8" - - platform: windows-latest - python-version: "pypy-2.7" + - platform: macos-latest + python-version: "pypy-3.9" steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e5dc5d..9ec8595 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,30 @@ **NOTE**: if any changes significantly impact your project or use case, please open an issue on [GitHub](https://github.com/GhostofGoes/getmac/issues) or email me (see git commit author info for address). +## UNRELEASED + +### Added +* Added support for Python 3.10 and 3.11 +* Added tentative support for Python 3.12 + +### Changed +* TBD + +### Removed +* Removed support for Python 2.7. It's time. Supporting 2.7 has become an onerous burden on the project, and has prevented fully supporting 3.10 and 3.11 due to having to stay on an ancient version of pytest. I'm done supporting a 13 year old version of a language. +* Removed support for Python 3.4 and 3.5. These are quite old and there were a number of essential features added in 3.6 that I'd like to use (f-strings, type annotations, mature `pathlib`, etc.) +* Removed support for Jython. As of Jan 23rd, 2023, [Jython](https://github.com/jython/jython) still does not support Python3, only 2.7. If and when it supports Python 3, I'll re-add support for it. +* Removed explicit support for IronPython. [IronPython3 exists](https://github.com/IronLanguages/ironpython3), however I don't have a way to test it in CI. If someone knows of a way to test it in GitHub actions, let me know, and I'm happy to re-add explicit support. + +### Dev +* Renamed `master` branch to `main` ## 0.9.0 (01/23/2023) **Announcement**: Compatibility with Python versions older than 3.6 (2.7, 3.4, and 3.5) is deprecated and will be removed in getmac 1.0.0. If you are stuck on an unsupported Python, considor loosely pinning the version of this package in your dependency list, e.g. `getmac<1`. This release is a *complete rewrite of getmac from the ground up*. The public API of `getmac` is **unchanged** as part of this rewrite. `get_mac_address()` is still the primary way of getting a MAC address, it's just the "under the hood" internals that have changed completely. - It's passing tests and seems to be operable. However, with a change this large there are ineviteably issues that the tests or I don't catch, so I'm doing a series of pre-releases until I'm 99% confident in it's stability. Refer to `docs/rewrite.md` for a in-depth explanation of the rewrite changes. + It's passing tests and seems to be operable. However, with a change this large there are inevitably issues that the tests or I don't catch, so I'm doing a series of pre-releases until I'm 99% confident in it's stability. Refer to `docs/rewrite.md` for a in-depth explanation of the rewrite changes. The new system has a number of benefits - Reduction of false-positives and false-negatives by improving method selection accuracy (platform, validity, etc.) @@ -28,7 +45,7 @@ The new system has a number of benefits * The amount of time taken to get a result (in seconds) will now be recorded and logged if debugging is enabled (`DEBUG>=1` or `-d`) * Added command line argument to override the UDP port for network requests: `--override-port` (this was already possible in Python via `getmac.getmac.PORT`, but wasn't configurable via the CLI. Now it is!). * Added ability to override the detected platform via `--override-platform` argument (CLI) or `getmac.getmac.OVERRIDE_PLATFORM` variable (Python). This will force methods for that platform to be used, regardless of the actual platform. Here's an example forcing `linux` to be used as the platform: `getmac -i eth0 --override-platform linux`. In version 1.0.0, this feature will added as an argument to `get_mac_address()`. -* Added ability to force a specific method to be used via `--force-method` argument (CLI) or `getmac.getmac.FORCE_METHOD` variable (Python). This is useful for troubleshotting issues, general debugging, and testing changes. Example: `getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost` +* Added ability to force a specific method to be used via `--force-method` argument (CLI) or `getmac.getmac.FORCE_METHOD` variable (Python). This is useful for troubleshooting issues, general debugging, and testing changes. Example: `getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost` ### Changed * **Complete rewrite of `getmac` from the ground up. Refer to `docs/rewrite.md` for a in-depth explanation of the rewrite changes** diff --git a/MANIFEST.in b/MANIFEST.in index 57fe3df..0afa6e5 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -10,6 +10,6 @@ prune scripts global-exclude *.py[cod] __pycache__ *.so *.class exclude *-getmac.spec *.yml -exclude tox.ini Dockerfile Vagrantfile .editorconfig .coveragerc +exclude tox.ini Dockerfile Vagrantfile .editorconfig exclude conf.py index.md make.bat Makefile rewrite.md releasing.md exclude style-requirements.txt requirements.txt TODO.md CODE_OF_CONDUCT.md SECURITY.md diff --git a/README.md b/README.md index 7513938..150b91f 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,6 @@ print(getmac.get_mac_address(ip="192.168.0.1", network_request=True)) ``` ## Terminal examples -**Python 2 users**: use `getmac2` or `python -m getmac` instead of `getmac`. ```bash getmac --help getmac --version @@ -130,10 +129,10 @@ getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost ## Features - Pure-Python (no compiled C-extensions required!) -- Python 2.7 and 3.4+ -- Lightweight, with no dependencies and a small package size -- Can be dropped into a project as a standalone .py file -- Supports most interpreters: CPython, pypy, pypy3, IronPython 2.7, and Jython 2.7 +- Python 3.6+ +- Lightweight, with no dependencies and a relatively small package size (no binary extensions) +- Supports most platforms with NO binary extensions required! +- Supports CPython and [PyPy](https://www.pypy.org/) - Provides a simple command line tool (when installed as a package) - MIT licensed! @@ -142,13 +141,15 @@ If you are running a old Python (2.6/3.3 and older) or interpreter, then you can - Python 2.5: get-mac 0.5.0 - Python 2.6: getmac 0.6.0 +- Python 2.7: getmac 0.9.0 - Python 3.2: get-mac 0.3.0 - Python 3.3: get-mac 0.3.0 +- Python 3.4: getmac 0.9.0 +- Python 3.5: getmac 0.9.0 NOTE: these versions do not have many of the performance improvements, platform support, and bug fixes that came with later releases. They generally work, just not as well. However, if you're using such an old Python, you probably don't care about all that :) ## Notes -- Python 3.10 and 3.11 should work, but are not automatically tested at the moment due to having to support 2.7 - If none of the arguments are selected, the default network interface for the system will be used. If the default network interface cannot be determined, then it will attempt to fallback to typical defaults for the platform (`Ethernet` on Windows, `em0` on BSD, `en0` on OSX/Darwin, and `eth0` otherwise). If that fails, then it will fallback to `lo` on POSIX systems. - "Remote hosts" refer to hosts in your local layer 2 network, also commonly referred to as a "broadcast domain", "LAN", or "VLAN". As far as I know, there is not a reliable method to get a MAC address for a remote host external to the LAN. If you know any methods otherwise, please [open a GitHub issue](https://github.com/GhostofGoes/getmac/issues) or shoot me an email, I'd love to be wrong about this. - The first four arguments are mutually exclusive. `network_request` does not have any functionality when the `interface` argument is specified, and can be safely set if using in a script. @@ -187,7 +188,7 @@ All or almost all features should work on "supported" platforms. While other ver - Server: TBD - Partially supported (untested): 2000, XP, Vista - Linux distros - - CentOS/RHEL 6+ (Only with Python 2.7+) + - CentOS/RHEL 6+ - Ubuntu 16.04+ (15.10 and older should work, but are untested) - Fedora (24+) - Mac OSX (Darwin) diff --git a/docs/TODO.md b/docs/TODO.md index 28ed81d..4888637 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -2,9 +2,12 @@ - [x] rename "master" branch to "main" - [x] Create 0.9.0 branch from master/main so we can submit patch releases if needed - [ ] Add guide on using the modules API, e.g. registering a new method in `getmac.getmac.METHODS`, etc. +- [ ] [issue #76](https://github.com/GhostofGoes/getmac/issues/76): get_mac_address() is caching an old mac address, no longer present in local ARP + - get_mac_address() is caching an old mac address for a given IP, even when it has timeout from OS ARP table. Only an explicit delete of the ARP entry on the OS make it return '00:00:00:00:00:00' again. + - Fix is to check that the flag != 0x0, which should do the trick, unless there's an edge case that it misses. - [ ] Switch to Poetry for project management - [ ] Also, add `isort` -- [ ] Support Python 3.10 +- [ ] Support Python 3.10 and 3.11 - [ ] Update pytest (pytest 4, which we were using to support python 2.7, doesn't work with python 3.10) - [ ] add tests + setup.py classifier - [ ] **API changes** (technically speaking) @@ -76,7 +79,7 @@ - [ ] Refactor to build a local state of the interfaces on the system, and use that as fallback for default lookup of interface with no name. Could also include MACs for faster lookup of future interface queries. Similar to how `netifaces` works, with a dict with interface infos. Properly address https://github.com/GhostofGoes/getmac/issues/78 ## Py3-related stuff for 1.0.0 -- [ ] Drop support for python 2.7, 3.4, and 3.5 +- [x] Drop support for python 2.7, 3.4, and 3.5 - [ ] BUMP TEST DEPENDENCIES AND PYTEST VERSION TO MODERN TIMES (especially pytest...) - [ ] Use Enums for platforms and method types instead of strings? - [ ] cache package imports done during test for use during `get()`, reuse @@ -86,9 +89,9 @@ - Move configurations for tools out of `tox.ini` and into `pyproject.toml` - Add codespell configuration, remove CLI arguments - Add linting of pyproject.toml, remove checking of setup.py -- [ ] update classifiers in setup.py +- [x] update classifiers in setup.py - [ ] add inline type annotations for method arguments. remove types from docstrings? -- [ ] Remove `shutilwhich.py` and `.coveragerc` +- [x] Remove `shutilwhich.py` and `.coveragerc` # Etc diff --git a/docs/conf.py b/docs/conf.py index e8217b2..0f6a93e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # diff --git a/getmac/__main__.py b/getmac/__main__.py index 2b51733..c67c178 100644 --- a/getmac/__main__.py +++ b/getmac/__main__.py @@ -1,5 +1,4 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- +#!/usr/bin/env python3 from __future__ import print_function diff --git a/getmac/getmac.py b/getmac/getmac.py index 6eaf88f..d9c9b4d 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -# http://web.archive.org/web/20140718071917/http://multivax.com/last_question.html - """ Get the MAC address of remote hosts or network interfaces. @@ -23,6 +20,7 @@ updated_mac = get_mac_address(ip="10.0.0.1", network_request=True) """ + import ctypes import logging import os @@ -34,12 +32,8 @@ import sys import traceback import warnings -from subprocess import CalledProcessError, check_output - -try: # Python 3 - from subprocess import DEVNULL # type: ignore -except ImportError: # Python 2 - DEVNULL = open(os.devnull, "wb") # type: ignore +from shutil import which +from subprocess import CalledProcessError, DEVNULL, check_output # Used for mypy (a data type analysis tool) # If you're copying the code, this section can be safely removed @@ -56,36 +50,15 @@ if not log.handlers: log.addHandler(logging.NullHandler()) -__version__ = "0.9.0" - -PY2 = sys.version_info[0] == 2 # type: bool +__version__ = "1.0.0a0" # Configurable settings DEBUG = 0 # type: int PORT = 55555 # type: int -# Monkeypatch shutil.which for python 2.7 (TODO(python3): remove shutilwhich.py) -if PY2: - from .shutilwhich import which -else: - from shutil import which - # Platform identifiers -if PY2: - _UNAME = platform.uname() # type: Tuple[str, str, str, str, str, str] - _SYST = _UNAME[0] # type: str -else: - _UNAME = platform.uname() # type: platform.uname_result - _SYST = _UNAME.system # type: str -if _SYST == "Java": - try: - import java.lang - - _SYST = str(java.lang.System.getProperty("os.name")) - except ImportError: - _java_err_msg = "Can't determine OS: couldn't import java.lang on Jython" - log.critical(_java_err_msg) - warnings.warn(_java_err_msg, RuntimeWarning) +_UNAME = platform.uname() # type: platform.uname_result +_SYST = _UNAME.system # type: str WINDOWS = _SYST == "Windows" # type: bool DARWIN = _SYST == "Darwin" # type: bool @@ -158,9 +131,6 @@ # This can also happen on other platforms, like Solaris MAC_RE_SHORT = r"([0-9a-fA-F]{1,2}(?::[0-9a-fA-F]{1,2}){5})" -# Ensure we only log the Python 2 warning once -WARNED_UNSUPPORTED_PYTHONS = False - # Cache of commands that have been checked for existence by check_command() CHECK_COMMAND_CACHE = {} # type: Dict[str, bool] @@ -183,12 +153,13 @@ def _clean_mac(mac): if mac is None: return None - # Handle cases where it's bytes (which are the same as str in PY2) - mac = str(mac) - if not PY2: # Strip bytestring conversion artifacts - # TODO(python3): check for bytes and decode instead of this weird hack - for garbage_string in ["b'", "'", "\\n", "\\r"]: - mac = mac.replace(garbage_string, "") + # Handle cases where it's bytes + if isinstance(mac, bytes): + mac = mac.decode("utf-8") + + # Strip bad characters + for garbage_string in ["\\n", "\\r"]: + mac = mac.replace(garbage_string, "") # Remove trailing whitespace, make lowercase, remove spaces, # and replace dashes '-' with colons ':'. @@ -230,8 +201,7 @@ def _read_file(filepath): try: with open(filepath) as f: return f.read() - # This is IOError on Python 2.7 - except (OSError, IOError): # noqa: B014 + except OSError: # noqa: B014 log.debug("Could not find file: '%s'", filepath) return None @@ -276,15 +246,15 @@ def _call_proc(executable, args): else: cmd = [executable] + shlex.split(args) # type: ignore - output = check_output(cmd, stderr=DEVNULL, env=ENV) + output = check_output(cmd, stderr=DEVNULL, env=ENV) # type: Union[bytes, str] if DEBUG >= 4: log.debug("Output from '%s' command: %s", executable, str(output)) - if not PY2 and isinstance(output, bytes): - return str(output, "utf-8") - else: - return str(output) + if isinstance(output, bytes): + output = output.decode("utf-8") + + return output def _uuid_convert(mac): @@ -301,11 +271,9 @@ def _fetch_ip_using_dns(): the default interface. This populates the source address of the socket, which we then inspect and return. """ - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s.connect(("1.1.1.1", 53)) - ip = s.getsockname()[0] - s.close() # NOTE: sockets don't have context manager in 2.7 :( - return ip + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: + s.connect(("1.1.1.1", 53)) + return s.getsockname()[0] class Method: @@ -639,8 +607,8 @@ def test(self): # type: () -> bool return False def get(self, arg): # type: (str) -> Optional[str] - if not PY2: # Convert to bytes on Python 3+ (Fixes GitHub issue #7) - arg = arg.encode() # type: ignore + # Convert to bytes on Python 3+ (Fixes GitHub issue #7) + arg = arg.encode() # type: ignore try: inetaddr = ctypes.windll.wsock32.inet_addr(arg) # type: ignore @@ -728,8 +696,7 @@ def test(self): # type: () -> bool def get(self, arg): # type: (str) -> Optional[str] from uuid import _find_mac # type: ignore - if not PY2: - arg = bytes(arg, "utf-8") # type: ignore + arg = bytes(arg, "utf-8") # type: ignore mac = _find_mac("lanscan", "-ai", [arg], lambda i: 0) @@ -754,18 +721,14 @@ def test(self): # type: () -> bool def get(self, arg): # type: (str) -> Optional[str] import fcntl - if not PY2: - arg = arg.encode() # type: ignore + arg = arg.encode() # type: ignore s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 0x8927 = SIOCGIFADDR info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack("256s", arg[:15])) - if PY2: - return ":".join(["%02x" % ord(char) for char in info[18:24]]) - else: - return ":".join(["%02x" % ord(chr(char)) for char in info[18:24]]) + return ":".join(["%02x" % ord(chr(char)) for char in info[18:24]]) class GetmacExe(Method): @@ -1713,19 +1676,6 @@ def get_mac_address( start_time = timeit.default_timer() - if PY2 or (sys.version_info[0] == 3 and sys.version_info[1] < 6): - global WARNED_UNSUPPORTED_PYTHONS - if not WARNED_UNSUPPORTED_PYTHONS: - warning_string = ( - "Support for Python versions < 3.6 is deprecated and will be " - "removed in getmac 1.0.0. If you are stuck on an unsupported " - "Python, considor loosely pinning the version of this package " - 'in your dependency list, e.g. "getmac<1".' - ) - warnings.warn(warning_string, DeprecationWarning) - log.warning(warning_string) # Ensure it appears in any logs - WARNED_UNSUPPORTED_PYTHONS = True - if (hostname and hostname == "localhost") or (ip and ip == "127.0.0.1"): return "00:00:00:00:00:00" diff --git a/getmac/shutilwhich.py b/getmac/shutilwhich.py deleted file mode 100644 index 771335a..0000000 --- a/getmac/shutilwhich.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding: utf-8 -*- -# This is a stopgap to maintain Python 2.7 support for the 0.9.0 release. -# -# Code copied from the "shutilwhich" project by Marc Brinkmann (@mbr) -# Source: https://github.com/mbr/shutilwhich -# -# We can't depend on it in setup.py since getmac has the __version__, -# which is imported in setup.py, which requires shutilwhich to be installed, -# leading to the typical problem: which came first, the Python or the Egg? :) -import os -import sys - - -# Everything below this point has been copied verbatim from the Python-3.3 -# sources. -def which(cmd, mode=os.F_OK | os.X_OK, path=None): - """Given a command, mode, and a PATH string, return the path which - conforms to the given mode on the PATH, or None if there is no such - file. - - `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result - of os.environ.get("PATH"), or can be overridden with a custom search - path. - - """ - - # Check that a given file can be accessed with the correct mode. - # Additionally check that `file` is not a directory, as on Windows - # directories pass the os.access check. - def _access_check(fn, mode): - return os.path.exists(fn) and os.access(fn, mode) and not os.path.isdir(fn) - - # Short circuit. If we're given a full path which matches the mode - # and it exists, we're done here. - if _access_check(cmd, mode): - return cmd - - path = (path or os.environ.get("PATH", os.defpath)).split(os.pathsep) - - if sys.platform == "win32": - # The current directory takes precedence on Windows. - if os.curdir not in path: - path.insert(0, os.curdir) - - # PATHEXT is necessary to check on Windows. - pathext = os.environ.get("PATHEXT", "").split(os.pathsep) - # See if the given file matches any of the expected path extensions. - # This will allow us to short circuit when given "python.exe". - matches = [cmd for ext in pathext if cmd.lower().endswith(ext.lower())] - # If it does match, only test that one, otherwise we have to try - # others. - files = [cmd] if matches else [cmd + ext.lower() for ext in pathext] - else: - # On other platforms you don't have things like PATHEXT to tell you - # what file suffixes are executable, so just pass on cmd as-is. - files = [cmd] - - seen = set() - for dir in path: - dir = os.path.normcase(dir) - if dir not in seen: - seen.add(dir) - for thefile in files: - name = os.path.join(dir, thefile) - if _access_check(name, mode): - return name - return None diff --git a/packaging/rpm/build-rpm-python2.sh b/packaging/rpm/build-rpm-python2.sh deleted file mode 100644 index f6d97d7..0000000 --- a/packaging/rpm/build-rpm-python2.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -SPECPATH="./packaging/python2-getmac.spec" - -sudo dnf install gcc rpm-build rpm-devel rpmlint make python bash coreutils diffutils patch rpmdevtools -rpmdev-setuptree -mkdir getmac-$1 -mkdir getmac-$1/docs -mkdir getmac-$1/docs/man -cp -rt getmac-$1 getmac setup.py README.md LICENSE -cp docs/man/getmac2.1 getmac-$1/docs/man -tar czf getmac-$1.tar.gz getmac-$1 -mv getmac-$1.tar.gz ~/rpmbuild/SOURCES/ -cp "$SPECPATH" ~/rpmbuild/SPECS/ -rm -rf getmac-$1 -cd ~/rpmbuild/SPECS/ -rpmbuild -bs python2-getmac.spec -rpmbuild -bb python2-getmac.spec diff --git a/packaging/rpm/man/getmac2.1 b/packaging/rpm/man/getmac2.1 deleted file mode 100644 index 6c042e9..0000000 --- a/packaging/rpm/man/getmac2.1 +++ /dev/null @@ -1,390 +0,0 @@ -.\" Man page generated from reStructuredText. -. -.TH "GETMAC" "1" "Oct 06, 2018" "0.6.0" "getmac" -.SH NAME -getmac \- Cross-platform Python package to get MAC addresses -. -.nr rst2man-indent-level 0 -. -.de1 rstReportMargin -\\$1 \\n[an-margin] -level \\n[rst2man-indent-level] -level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] -- -\\n[rst2man-indent0] -\\n[rst2man-indent1] -\\n[rst2man-indent2] -.. -.de1 INDENT -.\" .rstReportMargin pre: -. RS \\$1 -. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] -. nr rst2man-indent-level +1 -.\" .rstReportMargin post: -.. -.de UNINDENT -. RE -.\" indent \\n[an-margin] -.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] -.nr rst2man-indent-level -1 -.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] -.in \\n[rst2man-indent\\n[rst2man-indent-level]]u -.. -\fI\%Latest version on PyPI\fP -\fI\%Documentation status\fP -\fI\%Travis CI build status\fP -\fI\%Appveyor build status\fP -.sp -Get the MAC address of remote LAN hosts and system network interfaces using Python. -.sp -It provides a platform\-independant interface to get the MAC addresses of: -.INDENT 0.0 -.IP \(bu 2 -System network interfaces (by interface name) -.IP \(bu 2 -Remote hosts on the local network (by IPv4/IPv6 address or hostname) -.UNINDENT -.sp -It provides one function: \fBget_mac_address()\fP -.sp -\fI\%asciicast\fP -.sp -\fI\%asciicast\fP -.INDENT 0.0 -.IP \(bu 2 -Pure\-Python -.IP \(bu 2 -Supports Python 2.6+, 3.4+, pypy, and pypy3 -.IP \(bu 2 -No dependencies -.IP \(bu 2 -Small size -.IP \(bu 2 -Can be used as an independent .py file -.IP \(bu 2 -Simple terminal tool (when installed as a package) -.UNINDENT -.SH PYTHON EXAMPLES -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -from getmac import get_mac_address -eth_mac = get_mac_address(interface="eth0") -win_mac = get_mac_address(interface="Ethernet 3") -ip_mac = get_mac_address(ip="192.168.0.1") -ip6_mac = get_mac_address(ip6="::1") -host_mac = get_mac_address(hostname="localhost") -updated_mac = get_mac_address(ip="10.0.0.1", network_request=True) - -# Enabling debugging -from getmac import getmac -getmac.DEBUG = 2 # DEBUG level 2 -print(getmac.get_mac_address(interface="Ethernet 3")) - -# Changing the port used for updating ARP table (UDP packet) -from getmac import getmac -getmac.PORT = 44444 # Default: 55555 -print(get_mac_address(ip="192.168.0.1", network_request=True)) - -.ft P -.fi -.UNINDENT -.UNINDENT -.SH TERMINAL EXAMPLES -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -getmac \-\-help -getmac \-\-version - -# No arguments will return MAC of the default interface. -getmac -python \-m getmac - -# Interface names, IPv4/IPv6 addresses, or Hostnames can be specified -getmac \-\-interface ens33 -getmac \-\-ip 192.168.0.1 -getmac \-\-ip6 ::1 -getmac \-\-hostname home.router - -# Running as a Python module with shorthands for the arguments -python \-m getmac \-i \(aqEthernet 4\(aq -python \-m getmac \-4 192.168.0.1 -python \-m getmac \-6 ::1 -python \-m getmac \-n home.router - -# Getting the MAC address of a remote host obviously requires -# the ARP table to be populated. By default, getmac will do -# this for you by sending a small UDP packet to a high port (55555) -# If you don\(aqt want this to happen, you can disable it. -# This is useful if you\(aqre 100% certain the ARP table will be -# populated already, or in red team/forensic scenarios. -getmac \-\-no\-network\-request \-4 192.168.0.1 -python \-m getmac \-\-no\-network\-request \-n home.router - -# Debug levels can be specified with \(aq\-d\(aq -getmac \-\-debug -python \-m getmac \-d \-i enp11s4 -python \-m getmac \-dd \-n home.router - -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Note: the terminal interface will not work on Python 2.6 and older (Sorry RHEL 6 users!). -.SH GET_MAC_ADDRESS() -.INDENT 0.0 -.IP \(bu 2 -\fBinterface\fP: Name of a network interface on the system. -.IP \(bu 2 -\fBip\fP: IPv4 address of a remote host. -.IP \(bu 2 -\fBip6\fP: IPv6 address of a remote host. -.IP \(bu 2 -\fBhostname\fP: Hostname of a remote host. -.IP \(bu 2 -\fBnetwork_request\fP: If an network request should be made to update -and populate the ARP/NDP table of remote hosts used to lookup MACs -in most circumstances. Disable this if you want to just use what\(aqs -already in the table, or if you have requirements to prevent network -traffic. The network request is a empty UDP packet sent to a high -port, 55555 by default. This can be changed by setting \fBgetmac.PORT\fP -to the desired integer value. Additionally, on Windows, this will -send a UDP packet to 1.1.1.1:53 to attempt to determine the default interface. -.UNINDENT -.SH NOTES -.INDENT 0.0 -.IP \(bu 2 -If none of the arguments are selected, the default -network interface for the system will be used. -.IP \(bu 2 -"Remote hosts" refer to hosts in your local layer 2 network, also -commonly referred to as a "broadcast domain", "LAN", or "VLAN". As far -as I know, there is not a reliable method to get a MAC address for a -remote host external to the LAN. If you know any methods otherwise, please -open a GitHub issue or shoot me an email, I\(aqd love to be wrong about this. -.IP \(bu 2 -The first four arguments are mutually exclusive. \fBnetwork_request\fP -does not have any functionality when the \fBinterface\fP argument is -specified, and can be safely set if using in a script. -.IP \(bu 2 -The physical transport is assumed to be Ethernet (802.3). Others, such as -Wi\-Fi (802.11), are currently not tested or considored. I plan to -address this in the future, and am definitely open to pull requests -or issues related to this, including error reports. -.IP \(bu 2 -Exceptions will be handled silently and returned as a None. -If you run into problems, you can set DEBUG to true and get more -information about what\(aqs happening. If you\(aqre still having issues, -please create an issue on GitHub and include the output with DEBUG enabled. -.IP \(bu 2 -Messages are output using the \fBwarnings\fP module, and \fBprint()\fP if -\fBgetmac.DEBUG\fP enabled (any value greater than 0). -If you are using logging, they can be captured using logging.captureWarnings(). -Otherwise, they can be suppressed using warnings.filterwarnings("ignore"). -https://docs.python.org/3/library/warnings.html -.UNINDENT -.SH DOCKER EXAMPLES -.sp -Run getmac container and provide flags -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -docker run \-it ghostofgoes/getmac:latest \-\-help -docker run \-it ghostofgoes/getmac:latest \-\-version -docker run \-it ghostofgoes/getmac:latest \-n localhost - -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -There is a pre\-built container located on the -\fI\%Docker hub\fP\&. -Alternatively, you can build the image yourself (from the repository root directory): -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -docker build . \-t getmac - -.ft P -.fi -.UNINDENT -.UNINDENT -.INDENT 0.0 -.IP \(bu 2 -Windows -.INDENT 2.0 -.IP \(bu 2 -Commands: \fBgetmac\fP, \fBipconfig\fP -.IP \(bu 2 -Libraries: \fBuuid\fP, \fBctypes\fP -.IP \(bu 2 -Third\-party Packages: \fBnetifaces\fP, \fBpsutil\fP, \fBscapy\fP -.UNINDENT -.IP \(bu 2 -Linux/Unix -.INDENT 2.0 -.IP \(bu 2 -Commands: \fBarp\fP, \fBip\fP, \fBifconfig\fP, \fBnetstat\fP, \fBip link\fP -.IP \(bu 2 -Libraries: \fBuuid\fP, \fBfcntl\fP -.IP \(bu 2 -Third\-party Packages: \fBnetifaces\fP, \fBpsutil\fP, \fBscapy\fP, \fBarping\fP -.IP \(bu 2 -Default interfaces: \fBroute\fP, \fBip route list\fP -.IP \(bu 2 -Files: \fB/sys/class/net/X/address\fP, \fB/proc/net/arp\fP -.UNINDENT -.IP \(bu 2 -Mac OSX (Darwin) -.INDENT 2.0 -.IP \(bu 2 -\fBnetworksetup\fP -.IP \(bu 2 -Same commands as Linux -.UNINDENT -.IP \(bu 2 -WSL: Windows commands are used for remote hosts, -and Unix commands are used for interfaces -.UNINDENT -.sp -All or almost all features should work on "supported" platforms (OSes). -.INDENT 0.0 -.IP \(bu 2 -Windows -.INDENT 2.0 -.IP \(bu 2 -Desktop: 7, 8, 8.1, 10 -.IP \(bu 2 -Server: TBD -.IP \(bu 2 -(Partially supported, untested): 2000, XP, Vista -.UNINDENT -.IP \(bu 2 -Linux distros -.INDENT 2.0 -.IP \(bu 2 -CentOS/RHEL 6+ -.IP \(bu 2 -Ubuntu 16+ (14 and older should work as well) -.IP \(bu 2 -Fedora -.UNINDENT -.IP \(bu 2 -MacOSX (Darwin) -.INDENT 2.0 -.IP \(bu 2 -The latest two versions probably (TBD) -.UNINDENT -.IP \(bu 2 -Windows Subsystem for Linux (WSL) -.IP \(bu 2 -Docker -.UNINDENT -.sp -All sub\-versions are the latest available on your platform (with the exception of 2.6). -.INDENT 0.0 -.IP \(bu 2 -2.6.6 (CentOS 6/RHEL 6 version) -.IP \(bu 2 -2.7 -.IP \(bu 2 -3.4 -.IP \(bu 2 -3.5 -.IP \(bu 2 -3.6 -.IP \(bu 2 -3.7 -.UNINDENT -.sp -Please report any problems by opening a issue on GitHub! -.SH CAVEATS -.INDENT 0.0 -.IP \(bu 2 -Depending on the platform, there could be a performance detriment, -due to heavy usage of regular expressions. -.IP \(bu 2 -Platform test coverage is imperfect. If you\(aqre having issues, -then you might be using a platform I haven\(aqt been able to test. -Keep calm, open a GitHub issue, and I\(aqd be more than happy to help. -.IP \(bu 2 -Older Python versions (2.5/3.3 and older) are not officially supported. -If you\(aqre running these, all is not lost! Simply copy/paste \fBgetmac.py\fP -into your codebase and make the necessary edits to be compatible with -your version and distribution of Python. -.UNINDENT -.SH KNOWN ISSUES -.INDENT 0.0 -.IP \(bu 2 -Hostnames for IPv6 devices are not yet supported. -.IP \(bu 2 -Windows: the "default" of selecting the default route interface only -works effectively if \fBnetwork_request\fP is enabled. Otherwise, -\fBEthernet\fP as the default. -.IP \(bu 2 -There is are currently no automated tests for Python 2.6, which means -there is a much higher potential for regressions. Open an issue if you -encounter any. -.UNINDENT -.sp -Contributors are more than welcome! -See the \fI\%contribution guide\fP to get started, -and checkout the \fI\%todo list\fP for a full list of tasks and bugs. -.sp -Before submitting a PR, please make sure you\(aqve completed the -\fI\%pull request checklist\fP! -.sp -The \fI\%Python Discord server\fP is a good place -to ask questions or discuss the project (Handle: @KnownError). -.SH CONTRIBUTORS -.INDENT 0.0 -.IP \(bu 2 -Christopher Goes (@ghostofgoes) \- Author and maintainer -.IP \(bu 2 -Calvin Tran (@cyberhobbes) \- Windows interface detection improvements -.IP \(bu 2 -Izra Faturrahman (@Frizz925) \- Unit tests using the platform samples -.IP \(bu 2 -Jose Gonzalez (@Komish) \- Docker container and Docker testing -.IP \(bu 2 -@fortunate\-man \- Awesome usage videos -.IP \(bu 2 -@martmists \- legacy Python compatibility improvements -.UNINDENT -.sp -Many of the methods used to acquire an address and the core logic framework -are attributed to the CPython project\(aqs UUID implementation. -.INDENT 0.0 -.IP \(bu 2 -https://github.com/python/cpython/blob/master/Lib/uuid.py -.IP \(bu 2 -https://github.com/python/cpython/blob/2.7/Lib/uuid.py -.UNINDENT -.SH OTHER NOTABLE SOURCES -.INDENT 0.0 -.IP \(bu 2 -\fI\%_unix_fcntl_by_interface\fP -.IP \(bu 2 -\fI\%_windows_get_remote_mac_ctypes\fP -.IP \(bu 2 -\fI\%String joining\fP -.UNINDENT -.sp -MIT. Feel free to copy, modify, and use to your heart\(aqs content. Enjoy :) -.SH AUTHOR -Christopher Goes -.SH COPYRIGHT -2018, Christopher Goes -.\" Generated by docutils manpage writer. -. diff --git a/packaging/rpm/python2-getmac.spec b/packaging/rpm/python2-getmac.spec deleted file mode 100644 index 5808064..0000000 --- a/packaging/rpm/python2-getmac.spec +++ /dev/null @@ -1,187 +0,0 @@ -%global srcname getmac - -Name: python2-%{srcname} -Version: 0.6.0 -Release: 0 -Summary: Python module to get the MAC address of local network interfaces and LAN hosts - -License: MIT -URL: https://github.com/GhostofGoes/getmac -Source0: https://github.com/GhostofGoes/getmac/releases/download/0.6.0/getmac-0.6.0.tar.gz - -BuildArch: noarch -BuildRequires: python2-devel, python2-setuptools - -%description -Pure-python module to get the MAC address of remote hosts or network interfaces. -It provides a platform-independent interface to get the MAC addresses of network -interfaces on the local system(by interface name) and remote hosts on the local -network (by IPv4/IPv6 address or host-name). - -%{?python_provide:%python_provide python3-getmac} - -%prep -%autosetup -n %{srcname}-%{version} - -%build -%py2_build - -%install -%py2_install -%files -%license LICENSE -%doc README.md -%{python2_sitelib}/%{srcname}/ -%{python2_sitelib}/%{srcname}-*.egg-info/ -/usr/bin/getmac2 -%{_mandir}/man1/* - -%changelog -* Sat Oct 6 2018 Christopher Goes 0.6.0-0 -- Windows default interface detection if `network_request` is enabled (Credit: @cyberhobbes) -- Docker container (Credit: @Komish) -- Changed name to `getmac`. This applies to everything, including -command line tool, PyPI, GitHub, and the documentation. -This is a breaking change, but needed to happen to remove -a huge amount of ambiguity that was causing issues with packaging, -documentation, and several other efforts, not to mention my sanity. -Long-term, the only downside is a conflict on Windows CLI with `getmac.exe`. -- Use proper Python 2-compatible print functions (Credit: @martmists) -- Support for Python 2.5. It is not feasible to test, and potentially -breaks some useful language features, such as `__future__` -- Variables PORT and DEBUG from top-level package imports, since changing -them would have no actual effect on execution. Instead, use `getmac.getmac.DEBUG`. -- Added example videos demonstrating usage (Credit: @fortunate-man) -- Added contribution guide -- Added documentation on ReadTheDocs -- Added a manpage - -* Mon Sep 24 2018 Christopher Goes 0.5-0 -- Full support for Windows Subsystem for Linux (WSL). This is working for -all features, including default interface selection! The only edge case -is lookup of remote host IP addresses that are actually local interfaces -won't resolve to a MAC (which should be ff-ff-ff-ff-ff-ff). -- Require `argparse` if Python version is 2.6 or older -- Updated tox tests: added Jython and IronPython, removed 2.6 - -* Fri Sep 21 2018 Christopher Goes 0.4-0 -- New methods for remote host MACs -- New methods for interface MACs -- DEBUG levels: DEBUG value is now an integer, and increasing it will -increase the amount and verbosity of output. On the CLI, it can be -configured by increasing the amount of characters for the debug argument, -e.g. '-dd' for DEBUG level 2. -- Jython support (Note: on Windows Jython currently only works with interfaces) -- IronPython support -- Significant performance improvement for remote hosts. Previously, -the average for `get_mac_address(ip='10.0.0.100')` was 1.71 seconds. -Now, the average is `12.7 miliseconds`, with the special case of a unpopulated -arp table being only slightly higher. This was brought about by changes in -how the arp table is populated. The original method was to use the -host's `ping` command to send an ICMP packet to the host. This took time, -which heavily delayed the ability to actually get an address. The solution -is to instead simply send a empty UDP packet to a high port. The port -this packet is sent to can be configured using the module variable `getmac.PORT`. -- "Fixed" resolution of localhost/127.0.0.1 by hardcoding the response. -This should resolve a lot of problematic edge cases. I'm ok with this -for now since I don't know of a case when it isn't all zeroes. -- Greatly increased the reliability of getting host and interface MACs on Windows -- Improved debugging output -- Tightened up the size of `getmac.py` -- Various minor stability and performance improvements -- Add LICENSE to PyPI package -- Support for Python 3.2 and 3.3. The total downloads from PyPI with -those versions in August was ~53k and ~407K, respectfully. The majority -of those are likely from automated testing (e.g. TravisCI) and not -actual users. Therefore, I've decided to drop support to simplify -development, especially since before 3.4 the 3.x series was still -very much a "work in progress". -- Added automated tests for Windows using Appveyor -- Tox runner for tests -- Added github.io page -- Improved TravisCI testing - -* Thu Aug 30 2018 Christopher Goes 0.3-0 -- Attempt to use Python modules if they're installed. This is useful -for larger projects that already have them installed as dependencies, -as they provide a more reliable means of getting information. -- New methods for remote MACs -- New methods for Interface MACs -- Certain critical failures that should never happen will now warn -instead of failing silently. -- Added a sanity check to the `ip6` argument (IPv6 addresses) -- Improved performance in some areas -- Improved debugging output -- Major Bugfix: search of `proc/net/arp` would return shorter addresses in the -same subnet if they came earlier in the sequence. Example: a search for -`192.168.16.2` on Linux would instead return the MAC address of -`192.168.16.254` with no errors or warning whatsoever. -- Significantly improved default interface detection. Default -interfaces are now properly detected on Linux and most other -POSIX platforms with `ip` or `route` commands available, or the -`netifaces` Python module. -- Makefile -- Vagrantfile to spin up testing VMs for various platforms using [Vagrant](https://www.vagrantup.com/docs/) -- Added more samples of command output on platforms (Ubuntu 18.04 LTS) - -* Sun Aug 26 2018 Christopher Goes 0.2-4 -- Fixed identification of remote host on OSX -- Resolved hangs and noticeable lag that occurred when "network_request" -was True (the default) - - -* Tue Aug 7 2018 Christopher Goes 0.2-3 -- Remote host for Python 3 on Windows - -* Wed Apr 18 2018 Christopher Goes 0.2-2 -- Short versions of CLI arguments (e.g. "-i" for "--interface") -- Improved usage of "ping" across platforms and IP versions -- Various minor tweaks for performance -- Improved Windows detection -- Use of ping command with hostname -- Improvements to internal code - - -* Sun Apr 15 2018 Christopher Goes 0.2-1 -- Nothing changed. PyPI just won't let me push changes without a new version. - -* Sun Apr 15 2018 Christopher Goes 0.2-0 -- Checks for default interface on Linux systems -- New methods of hunting for addresses on Windows, Mac OS X, and Linux -- CLI will output nothing if it failed, instead of "None" -- CLI will return with 1 on failure, 0 on success -- No CLI arguments now implies the default host network interface -- Added an argumnent for debugging: `--debug` -- Removed `-d` option from `--no-network-requests` -- Interfaces on Windows and Linux (including Bash for Windows) -- Many bugs -- Support for Python 2.6 on the CLI -- Overhaul of internals - - -* Sun Apr 15 2018 Christopher Goes 0.1-0: -- Addition of a terminal command: `get-mac` -- Ability to run as a module from the command line: `python -m getmac` -- `arp_request` argument was renamed to `network_request` -- Updated docstring -- Slight reduction in the size of getmac.py -- Overhauled the README -- Moved tests into their own folder -- Added Python 3.7 to list of supported snakes - - -* Sun Nov 12 2017 Christopher Goes 0.0-4: -- Python 2.6 compatibility - - -* Sat Nov 11 2017 Christopher Goes 0.0-3: -- Fixed some addresses returning without colons -- Added more rigorous checks on addresses before returning them - - -* Sat Nov 11 2017 Christopher Goes 0.0-2: -- Remove print statements and other debugging output - - -* Mon Oct 23 2017 Christopher Goes 0.0-1 -- Initial pre-alpha diff --git a/scripts/centos-provision.sh b/scripts/centos-provision.sh index 73690a1..a6c2785 100644 --- a/scripts/centos-provision.sh +++ b/scripts/centos-provision.sh @@ -24,4 +24,3 @@ git clone https://github.com/GhostofGoes/getmac.git # Create Python virtual environments mkdir -p "$HOME/.virtualenvs/" python3.4 -m virtualenv --python=python3.4 "$HOME/.virtualenvs/getmac34" -python -m virtualenv --python=python2 "$HOME/.virtualenvs/getmac27" diff --git a/scripts/openbsd-provision.sh b/scripts/openbsd-provision.sh index 9afc611..c96c4b5 100644 --- a/scripts/openbsd-provision.sh +++ b/scripts/openbsd-provision.sh @@ -8,5 +8,4 @@ sudo pkg_add python-2.7.15p0 python-3.6.6p1 \ # http://openbsd-archive.7691.n7.nabble.com/Best-Practices-python-virtualenv-td342219.html sudo mkdir -p "/usr/local/.virtualenvs/" sudo chown vagrant: /usr/local/.virtualenvs/ -python3 -m venv "/usr/local/.virtualenvs/getmac36" -python2 -m virtualenv "/usr/local/.virtualenvs/getmac27" +python3 -m venv "/usr/local/.virtualenvs/getmac" diff --git a/scripts/ubuntu-provision.sh b/scripts/ubuntu-provision.sh index 0889df9..c5b9162 100644 --- a/scripts/ubuntu-provision.sh +++ b/scripts/ubuntu-provision.sh @@ -15,5 +15,4 @@ git clone https://github.com/GhostofGoes/getmac.git # Create Python virtual environments mkdir -p "$HOME/.virtualenvs/" -python3 -m virtualenv --python=python3 "$HOME/.virtualenvs/getmac3" -python -m virtualenv --python=python2 "$HOME/.virtualenvs/getmac27" +python3 -m virtualenv --python=python3 "$HOME/.virtualenvs/getmac" diff --git a/setup.py b/setup.py index c4eace5..7940ff1 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,4 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import sys -from io import open # Python 2 compatibility +#!/usr/bin/env python3 from setuptools import setup @@ -37,11 +33,9 @@ data_files=[], packages=["getmac"], zip_safe=True, - entry_points={"console_scripts": ["getmac2 = getmac.__main__:main"]} - if sys.version_info[:2] <= (2, 7) - else {"console_scripts": ["getmac = getmac.__main__:main"]}, + entry_points={"console_scripts": ["getmac = getmac.__main__:main"]}, install_requires=[], - python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", + python_requires=">=3.6", keywords=[ "getmac", "get-mac", @@ -57,7 +51,7 @@ "layer-2", "802.3", ], - classifiers=[ # TODO(python3): update classifiers + classifiers=[ # TODO(1.0.0): change to 5 - Production/Stable "Development Status :: 4 - Beta", "Environment :: Console", @@ -65,21 +59,24 @@ "Operating System :: OS Independent", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", + "Operating System :: POSIX :: BSD", + "Operating System :: POSIX :: BSD :: FreeBSD", + "Operating System :: POSIX :: BSD :: NetBSD", + "Operating System :: POSIX :: BSD :: OpenBSD", + "Operating System :: POSIX :: Linux", + "Operating System :: POSIX :: Other", "Operating System :: MacOS", "Programming Language :: Python", "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", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", - "Programming Language :: Python :: Implementation :: IronPython", - "Programming Language :: Python :: Implementation :: Jython", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "Intended Audience :: Information Technology", @@ -88,5 +85,6 @@ "Topic :: System :: Systems Administration", "Topic :: System :: Networking", "Topic :: Utilities", + "Typing :: Typed", ], ) diff --git a/tests/conftest.py b/tests/conftest.py index 270dd70..9f7d90e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import io from os import path diff --git a/tests/test-requirements.txt b/tests/test-requirements.txt index 0963ffd..d497c96 100644 --- a/tests/test-requirements.txt +++ b/tests/test-requirements.txt @@ -1,8 +1,7 @@ -pytest<5 -pytest-randomly==1.2.3 -pytest-mock==1.10.4 -pytest-benchmark==3.2.2 -pytest-cov==2.7.1 -# TODO: older pytest-xdist isn't working on windows -# pytest-xdist==1.34.0 -# psutil +pytest +pytest-randomly +pytest-mock +pytest-benchmark +pytest-cov +pytest-xdist +psutil diff --git a/tests/test_cli.py b/tests/test_cli.py index cfbe0ef..a1be3ac 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import platform import sys from subprocess import PIPE, Popen @@ -8,7 +6,6 @@ from getmac import __version__, get_mac_address -PY2 = sys.version_info[0] == 2 BASE_CMD = [sys.executable, "-m", "getmac"] @@ -38,11 +35,6 @@ def test_cli_help(): assert run_cmd(BASE_CMD + ["--help"]) != "" -@pytest.mark.skipif( - PY2, - reason="This doesn't work in Python 2.7 " - "and I don't care enough to figure out why", -) def test_cli_version(): assert __version__ in run_cmd(BASE_CMD + ["--version"]) diff --git a/tests/test_getmac.py b/tests/test_getmac.py index d483034..d7284d9 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import inspect import sys from subprocess import CalledProcessError @@ -8,7 +6,6 @@ from getmac import get_mac_address, getmac -PY2 = sys.version_info[0] == 2 MAC_RE_COLON = r"([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})" @@ -84,10 +81,7 @@ def test_uuid_convert(): def test_read_file_return(mocker, get_sample): data = get_sample("ifconfig.out") mock_open = mocker.mock_open(read_data=data) - if PY2: - mocker.patch("__builtin__.open", mock_open) - else: - mocker.patch("builtins.open", mock_open) + mocker.patch("builtins.open", mock_open) assert getmac._read_file("ifconfig.out") == data mock_open.assert_called_once_with("ifconfig.out") diff --git a/tests/test_methods.py b/tests/test_methods.py index 165bd34..16eaa4c 100644 --- a/tests/test_methods.py +++ b/tests/test_methods.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import platform import socket import sys diff --git a/tox.ini b/tox.ini index 68d1cdc..ad2f4bd 100644 --- a/tox.ini +++ b/tox.ini @@ -1,23 +1,22 @@ [tox] -; NOTE: jython doesn't play well with pyenv and tox -envlist = py{27,34,35,36,37,38,39,py}-{linux,macos,windows},ironpython-windows,pypy3-{linux,macos,windows} +envlist = py{36,37,38,39,310,311,312,py3}-{linux,macos,windows} skip_missing_interpreters = true [gh-actions] python = - 2.7: py27 - 3.4: py34 - 3.5: py35 3.6: py36 3.7: py37 3.8: py38 3.9: py39 3.10: py310 3.11: py311 - pypy-2.7: pypy + 3.12: py312 pypy-3.6: pypy3 pypy-3.7: pypy3 pypy-3.8: pypy3 + pypy-3.9: pypy3 + pypy-3.10: pypy3 + pypy-3.11: pypy3 [gh-actions:env] PLATFORM = @@ -41,16 +40,14 @@ platform = deps = -rtests/test-requirements.txt ; TODO: --benchmark-enable? commands = - ; -n auto (TODO: older pytest-xdist isn't working on windows) pytest --basetemp={envtmpdir} --benchmark-disable --cov=getmac \ --cov-report term --cov-report html --cov-report xml \ - --cov-config=.coveragerc {posargs} + -n auto {posargs} [testenv:benchmark] description = "Run the test suite with benchmarks enabled" commands = - ; -n auto (TODO: older pytest-xdist isn't working on windows) - pytest --basetemp={envtmpdir} --benchmark-enable {posargs} + pytest --basetemp={envtmpdir} --benchmark-enable -n auto {posargs} [testenv:check] description = "Run code quality, static analysis, and spelling checks" @@ -58,7 +55,7 @@ setenv = PIP_DISABLE_PIP_VERSION_CHECK = 1 MYPYPATH=getmac deps = -rtests/style-requirements.txt -basepython = py3 +basepython = py311, py310, py3 commands = codespell --check-filenames --skip="*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,*.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist,*getmac2.1,*getmac.1,./packaging/rpm/man/*" --ignore-words-list "ether,Expresso,expresso,Espresso,espresso" flake8 getmac tests setup.py @@ -70,7 +67,7 @@ commands = [flake8] max-line-length = 99 application-import-names = getmac -exclude = .github, .vagrant, docs, packaging, samples, scripts, *.egg-info, __pycache__, .git, .tox, .venv getmac/shutilwhich.py +exclude = .github, .vagrant, docs, packaging, samples, scripts, *.egg-info, __pycache__, .git, .tox, .venv ignore = E203, W503, E401, F401, E226, B902, ANN101, ANN102, T499 per-file-ignores = tests/*.py:ANN001,ANN002,ANN003,ANN201,ANN202 @@ -79,10 +76,12 @@ show-source = True max-complexity = 38 [pytest] -minversion = 3.4 testpaths = tests norecursedirs = .github getmac samples scripts .git .tox .vagrant .idea .vscode dist build *.egg .* .venv addopts = --tb=short filterwarnings = ignore::DeprecationWarning + +[coverage:run] +relative_files = True From 1284798a0ba35d1bd49e16d2817dd540d0e58391 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 23 Jan 2023 17:11:13 -0600 Subject: [PATCH 002/112] Remove old RPM packaging --- CHANGELOG.md | 1 + packaging/rpm/build-rpm-python3.sh | 18 -- packaging/rpm/man/getmac.1 | 390 ----------------------------- packaging/rpm/python3-getmac.spec | 187 -------------- tox.ini | 2 +- 5 files changed, 2 insertions(+), 596 deletions(-) delete mode 100644 packaging/rpm/build-rpm-python3.sh delete mode 100644 packaging/rpm/man/getmac.1 delete mode 100644 packaging/rpm/python3-getmac.spec diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ec8595..ef309c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Removed support for Python 3.4 and 3.5. These are quite old and there were a number of essential features added in 3.6 that I'd like to use (f-strings, type annotations, mature `pathlib`, etc.) * Removed support for Jython. As of Jan 23rd, 2023, [Jython](https://github.com/jython/jython) still does not support Python3, only 2.7. If and when it supports Python 3, I'll re-add support for it. * Removed explicit support for IronPython. [IronPython3 exists](https://github.com/IronLanguages/ironpython3), however I don't have a way to test it in CI. If someone knows of a way to test it in GitHub actions, let me know, and I'm happy to re-add explicit support. +* Removed RPM packaging. This wasn't being used anywhere, and hasn't been updated since version 0.6.0. Way back when, I dreamt of putting this in OS package repositories. However, I've now learned that's up to the maintainers of those repos (e.g., Ubuntu or Fedora maintainers), not me. ### Dev * Renamed `master` branch to `main` diff --git a/packaging/rpm/build-rpm-python3.sh b/packaging/rpm/build-rpm-python3.sh deleted file mode 100644 index 93e746f..0000000 --- a/packaging/rpm/build-rpm-python3.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -SPECPATH="./packaging/python3-getmac.spec" - -sudo dnf install gcc rpm-build rpm-devel rpmlint make python bash coreutils diffutils patch rpmdevtools -rpmdev-setuptree -mkdir getmac-$1 -mkdir getmac-$1/docs -mkdir getmac-$1/docs/man -cp -rt getmac-$1 getmac setup.py README.md LICENSE -cp docs/man/getmac.1 getmac-$1/docs/man -tar czf getmac-$1.tar.gz getmac-$1 -mv getmac-$1.tar.gz ~/rpmbuild/SOURCES/ -cp "$SPECPATH" ~/rpmbuild/SPECS/ -rm -rf getmac-$1 -cd ~/rpmbuild/SPECS/ -rpmbuild -bs python3-getmac.spec -rpmbuild -bb python3-getmac.spec diff --git a/packaging/rpm/man/getmac.1 b/packaging/rpm/man/getmac.1 deleted file mode 100644 index 6c042e9..0000000 --- a/packaging/rpm/man/getmac.1 +++ /dev/null @@ -1,390 +0,0 @@ -.\" Man page generated from reStructuredText. -. -.TH "GETMAC" "1" "Oct 06, 2018" "0.6.0" "getmac" -.SH NAME -getmac \- Cross-platform Python package to get MAC addresses -. -.nr rst2man-indent-level 0 -. -.de1 rstReportMargin -\\$1 \\n[an-margin] -level \\n[rst2man-indent-level] -level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] -- -\\n[rst2man-indent0] -\\n[rst2man-indent1] -\\n[rst2man-indent2] -.. -.de1 INDENT -.\" .rstReportMargin pre: -. RS \\$1 -. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] -. nr rst2man-indent-level +1 -.\" .rstReportMargin post: -.. -.de UNINDENT -. RE -.\" indent \\n[an-margin] -.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] -.nr rst2man-indent-level -1 -.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] -.in \\n[rst2man-indent\\n[rst2man-indent-level]]u -.. -\fI\%Latest version on PyPI\fP -\fI\%Documentation status\fP -\fI\%Travis CI build status\fP -\fI\%Appveyor build status\fP -.sp -Get the MAC address of remote LAN hosts and system network interfaces using Python. -.sp -It provides a platform\-independant interface to get the MAC addresses of: -.INDENT 0.0 -.IP \(bu 2 -System network interfaces (by interface name) -.IP \(bu 2 -Remote hosts on the local network (by IPv4/IPv6 address or hostname) -.UNINDENT -.sp -It provides one function: \fBget_mac_address()\fP -.sp -\fI\%asciicast\fP -.sp -\fI\%asciicast\fP -.INDENT 0.0 -.IP \(bu 2 -Pure\-Python -.IP \(bu 2 -Supports Python 2.6+, 3.4+, pypy, and pypy3 -.IP \(bu 2 -No dependencies -.IP \(bu 2 -Small size -.IP \(bu 2 -Can be used as an independent .py file -.IP \(bu 2 -Simple terminal tool (when installed as a package) -.UNINDENT -.SH PYTHON EXAMPLES -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -from getmac import get_mac_address -eth_mac = get_mac_address(interface="eth0") -win_mac = get_mac_address(interface="Ethernet 3") -ip_mac = get_mac_address(ip="192.168.0.1") -ip6_mac = get_mac_address(ip6="::1") -host_mac = get_mac_address(hostname="localhost") -updated_mac = get_mac_address(ip="10.0.0.1", network_request=True) - -# Enabling debugging -from getmac import getmac -getmac.DEBUG = 2 # DEBUG level 2 -print(getmac.get_mac_address(interface="Ethernet 3")) - -# Changing the port used for updating ARP table (UDP packet) -from getmac import getmac -getmac.PORT = 44444 # Default: 55555 -print(get_mac_address(ip="192.168.0.1", network_request=True)) - -.ft P -.fi -.UNINDENT -.UNINDENT -.SH TERMINAL EXAMPLES -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -getmac \-\-help -getmac \-\-version - -# No arguments will return MAC of the default interface. -getmac -python \-m getmac - -# Interface names, IPv4/IPv6 addresses, or Hostnames can be specified -getmac \-\-interface ens33 -getmac \-\-ip 192.168.0.1 -getmac \-\-ip6 ::1 -getmac \-\-hostname home.router - -# Running as a Python module with shorthands for the arguments -python \-m getmac \-i \(aqEthernet 4\(aq -python \-m getmac \-4 192.168.0.1 -python \-m getmac \-6 ::1 -python \-m getmac \-n home.router - -# Getting the MAC address of a remote host obviously requires -# the ARP table to be populated. By default, getmac will do -# this for you by sending a small UDP packet to a high port (55555) -# If you don\(aqt want this to happen, you can disable it. -# This is useful if you\(aqre 100% certain the ARP table will be -# populated already, or in red team/forensic scenarios. -getmac \-\-no\-network\-request \-4 192.168.0.1 -python \-m getmac \-\-no\-network\-request \-n home.router - -# Debug levels can be specified with \(aq\-d\(aq -getmac \-\-debug -python \-m getmac \-d \-i enp11s4 -python \-m getmac \-dd \-n home.router - -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -Note: the terminal interface will not work on Python 2.6 and older (Sorry RHEL 6 users!). -.SH GET_MAC_ADDRESS() -.INDENT 0.0 -.IP \(bu 2 -\fBinterface\fP: Name of a network interface on the system. -.IP \(bu 2 -\fBip\fP: IPv4 address of a remote host. -.IP \(bu 2 -\fBip6\fP: IPv6 address of a remote host. -.IP \(bu 2 -\fBhostname\fP: Hostname of a remote host. -.IP \(bu 2 -\fBnetwork_request\fP: If an network request should be made to update -and populate the ARP/NDP table of remote hosts used to lookup MACs -in most circumstances. Disable this if you want to just use what\(aqs -already in the table, or if you have requirements to prevent network -traffic. The network request is a empty UDP packet sent to a high -port, 55555 by default. This can be changed by setting \fBgetmac.PORT\fP -to the desired integer value. Additionally, on Windows, this will -send a UDP packet to 1.1.1.1:53 to attempt to determine the default interface. -.UNINDENT -.SH NOTES -.INDENT 0.0 -.IP \(bu 2 -If none of the arguments are selected, the default -network interface for the system will be used. -.IP \(bu 2 -"Remote hosts" refer to hosts in your local layer 2 network, also -commonly referred to as a "broadcast domain", "LAN", or "VLAN". As far -as I know, there is not a reliable method to get a MAC address for a -remote host external to the LAN. If you know any methods otherwise, please -open a GitHub issue or shoot me an email, I\(aqd love to be wrong about this. -.IP \(bu 2 -The first four arguments are mutually exclusive. \fBnetwork_request\fP -does not have any functionality when the \fBinterface\fP argument is -specified, and can be safely set if using in a script. -.IP \(bu 2 -The physical transport is assumed to be Ethernet (802.3). Others, such as -Wi\-Fi (802.11), are currently not tested or considored. I plan to -address this in the future, and am definitely open to pull requests -or issues related to this, including error reports. -.IP \(bu 2 -Exceptions will be handled silently and returned as a None. -If you run into problems, you can set DEBUG to true and get more -information about what\(aqs happening. If you\(aqre still having issues, -please create an issue on GitHub and include the output with DEBUG enabled. -.IP \(bu 2 -Messages are output using the \fBwarnings\fP module, and \fBprint()\fP if -\fBgetmac.DEBUG\fP enabled (any value greater than 0). -If you are using logging, they can be captured using logging.captureWarnings(). -Otherwise, they can be suppressed using warnings.filterwarnings("ignore"). -https://docs.python.org/3/library/warnings.html -.UNINDENT -.SH DOCKER EXAMPLES -.sp -Run getmac container and provide flags -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -docker run \-it ghostofgoes/getmac:latest \-\-help -docker run \-it ghostofgoes/getmac:latest \-\-version -docker run \-it ghostofgoes/getmac:latest \-n localhost - -.ft P -.fi -.UNINDENT -.UNINDENT -.sp -There is a pre\-built container located on the -\fI\%Docker hub\fP\&. -Alternatively, you can build the image yourself (from the repository root directory): -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -docker build . \-t getmac - -.ft P -.fi -.UNINDENT -.UNINDENT -.INDENT 0.0 -.IP \(bu 2 -Windows -.INDENT 2.0 -.IP \(bu 2 -Commands: \fBgetmac\fP, \fBipconfig\fP -.IP \(bu 2 -Libraries: \fBuuid\fP, \fBctypes\fP -.IP \(bu 2 -Third\-party Packages: \fBnetifaces\fP, \fBpsutil\fP, \fBscapy\fP -.UNINDENT -.IP \(bu 2 -Linux/Unix -.INDENT 2.0 -.IP \(bu 2 -Commands: \fBarp\fP, \fBip\fP, \fBifconfig\fP, \fBnetstat\fP, \fBip link\fP -.IP \(bu 2 -Libraries: \fBuuid\fP, \fBfcntl\fP -.IP \(bu 2 -Third\-party Packages: \fBnetifaces\fP, \fBpsutil\fP, \fBscapy\fP, \fBarping\fP -.IP \(bu 2 -Default interfaces: \fBroute\fP, \fBip route list\fP -.IP \(bu 2 -Files: \fB/sys/class/net/X/address\fP, \fB/proc/net/arp\fP -.UNINDENT -.IP \(bu 2 -Mac OSX (Darwin) -.INDENT 2.0 -.IP \(bu 2 -\fBnetworksetup\fP -.IP \(bu 2 -Same commands as Linux -.UNINDENT -.IP \(bu 2 -WSL: Windows commands are used for remote hosts, -and Unix commands are used for interfaces -.UNINDENT -.sp -All or almost all features should work on "supported" platforms (OSes). -.INDENT 0.0 -.IP \(bu 2 -Windows -.INDENT 2.0 -.IP \(bu 2 -Desktop: 7, 8, 8.1, 10 -.IP \(bu 2 -Server: TBD -.IP \(bu 2 -(Partially supported, untested): 2000, XP, Vista -.UNINDENT -.IP \(bu 2 -Linux distros -.INDENT 2.0 -.IP \(bu 2 -CentOS/RHEL 6+ -.IP \(bu 2 -Ubuntu 16+ (14 and older should work as well) -.IP \(bu 2 -Fedora -.UNINDENT -.IP \(bu 2 -MacOSX (Darwin) -.INDENT 2.0 -.IP \(bu 2 -The latest two versions probably (TBD) -.UNINDENT -.IP \(bu 2 -Windows Subsystem for Linux (WSL) -.IP \(bu 2 -Docker -.UNINDENT -.sp -All sub\-versions are the latest available on your platform (with the exception of 2.6). -.INDENT 0.0 -.IP \(bu 2 -2.6.6 (CentOS 6/RHEL 6 version) -.IP \(bu 2 -2.7 -.IP \(bu 2 -3.4 -.IP \(bu 2 -3.5 -.IP \(bu 2 -3.6 -.IP \(bu 2 -3.7 -.UNINDENT -.sp -Please report any problems by opening a issue on GitHub! -.SH CAVEATS -.INDENT 0.0 -.IP \(bu 2 -Depending on the platform, there could be a performance detriment, -due to heavy usage of regular expressions. -.IP \(bu 2 -Platform test coverage is imperfect. If you\(aqre having issues, -then you might be using a platform I haven\(aqt been able to test. -Keep calm, open a GitHub issue, and I\(aqd be more than happy to help. -.IP \(bu 2 -Older Python versions (2.5/3.3 and older) are not officially supported. -If you\(aqre running these, all is not lost! Simply copy/paste \fBgetmac.py\fP -into your codebase and make the necessary edits to be compatible with -your version and distribution of Python. -.UNINDENT -.SH KNOWN ISSUES -.INDENT 0.0 -.IP \(bu 2 -Hostnames for IPv6 devices are not yet supported. -.IP \(bu 2 -Windows: the "default" of selecting the default route interface only -works effectively if \fBnetwork_request\fP is enabled. Otherwise, -\fBEthernet\fP as the default. -.IP \(bu 2 -There is are currently no automated tests for Python 2.6, which means -there is a much higher potential for regressions. Open an issue if you -encounter any. -.UNINDENT -.sp -Contributors are more than welcome! -See the \fI\%contribution guide\fP to get started, -and checkout the \fI\%todo list\fP for a full list of tasks and bugs. -.sp -Before submitting a PR, please make sure you\(aqve completed the -\fI\%pull request checklist\fP! -.sp -The \fI\%Python Discord server\fP is a good place -to ask questions or discuss the project (Handle: @KnownError). -.SH CONTRIBUTORS -.INDENT 0.0 -.IP \(bu 2 -Christopher Goes (@ghostofgoes) \- Author and maintainer -.IP \(bu 2 -Calvin Tran (@cyberhobbes) \- Windows interface detection improvements -.IP \(bu 2 -Izra Faturrahman (@Frizz925) \- Unit tests using the platform samples -.IP \(bu 2 -Jose Gonzalez (@Komish) \- Docker container and Docker testing -.IP \(bu 2 -@fortunate\-man \- Awesome usage videos -.IP \(bu 2 -@martmists \- legacy Python compatibility improvements -.UNINDENT -.sp -Many of the methods used to acquire an address and the core logic framework -are attributed to the CPython project\(aqs UUID implementation. -.INDENT 0.0 -.IP \(bu 2 -https://github.com/python/cpython/blob/master/Lib/uuid.py -.IP \(bu 2 -https://github.com/python/cpython/blob/2.7/Lib/uuid.py -.UNINDENT -.SH OTHER NOTABLE SOURCES -.INDENT 0.0 -.IP \(bu 2 -\fI\%_unix_fcntl_by_interface\fP -.IP \(bu 2 -\fI\%_windows_get_remote_mac_ctypes\fP -.IP \(bu 2 -\fI\%String joining\fP -.UNINDENT -.sp -MIT. Feel free to copy, modify, and use to your heart\(aqs content. Enjoy :) -.SH AUTHOR -Christopher Goes -.SH COPYRIGHT -2018, Christopher Goes -.\" Generated by docutils manpage writer. -. diff --git a/packaging/rpm/python3-getmac.spec b/packaging/rpm/python3-getmac.spec deleted file mode 100644 index e0d84e8..0000000 --- a/packaging/rpm/python3-getmac.spec +++ /dev/null @@ -1,187 +0,0 @@ -%global srcname getmac - -Name: python3-%{srcname} -Version: 0.6.0 -Release: 0 -Summary: Python module to get the MAC address of local network interfaces and LAN hosts - -License: MIT -URL: https://github.com/GhostofGoes/getmac -Source0: https://github.com/GhostofGoes/getmac/releases/download/0.6.0/getmac-0.6.0.tar.gz - -BuildArch: noarch -BuildRequires: python3-devel, python3-setuptools - -%description -Pure-python module to get the MAC address of remote hosts or network interfaces. -It provides a platform-independent interface to get the MAC addresses of network -interfaces on the local system(by interface name) and remote hosts on the local -network (by IPv4/IPv6 address or host-name). - -%{?python_provide:%python_provide python3-getmac} - -%prep -%autosetup -n %{srcname}-%{version} - -%build -%py3_build - -%install -%py3_install -%files -%license LICENSE -%doc README.md -%{python3_sitelib}/%{srcname}/ -%{python3_sitelib}/%{srcname}-*.egg-info/ -/usr/bin/getmac -%{_mandir}/man1/* - -%changelog -* Sat Oct 6 2018 Christopher Goes 0.6.0-0 -- Windows default interface detection if `network_request` is enabled (Credit: @cyberhobbes) -- Docker container (Credit: @Komish) -- Changed name to `getmac`. This applies to everything, including -command line tool, PyPI, GitHub, and the documentation. -This is a breaking change, but needed to happen to remove -a huge amount of ambiguity that was causing issues with packaging, -documentation, and several other efforts, not to mention my sanity. -Long-term, the only downside is a conflict on Windows CLI with `getmac.exe`. -- Use proper Python 2-compatible print functions (Credit: @martmists) -- Support for Python 2.5. It is not feasible to test, and potentially -breaks some useful language features, such as `__future__` -- Variables PORT and DEBUG from top-level package imports, since changing -them would have no actual effect on execution. Instead, use `getmac.getmac.DEBUG`. -- Added example videos demonstrating usage (Credit: @fortunate-man) -- Added contribution guide -- Added documentation on ReadTheDocs -- Added a manpage - -* Mon Sep 24 2018 Christopher Goes 0.5-0 -- Full support for Windows Subsystem for Linux (WSL). This is working for -all features, including default interface selection! The only edge case -is lookup of remote host IP addresses that are actually local interfaces -won't resolve to a MAC (which should be ff-ff-ff-ff-ff-ff). -- Require `argparse` if Python version is 2.6 or older -- Updated tox tests: added Jython and IronPython, removed 2.6 - -* Fri Sep 21 2018 Christopher Goes 0.4-0 -- New methods for remote host MACs -- New methods for interface MACs -- DEBUG levels: DEBUG value is now an integer, and increasing it will -increase the amount and verbosity of output. On the CLI, it can be -configured by increasing the amount of characters for the debug argument, -e.g. '-dd' for DEBUG level 2. -- Jython support (Note: on Windows Jython currently only works with interfaces) -- IronPython support -- Significant performance improvement for remote hosts. Previously, -the average for `get_mac_address(ip='10.0.0.100')` was 1.71 seconds. -Now, the average is `12.7 miliseconds`, with the special case of a unpopulated -arp table being only slightly higher. This was brought about by changes in -how the arp table is populated. The original method was to use the -host's `ping` command to send an ICMP packet to the host. This took time, -which heavily delayed the ability to actually get an address. The solution -is to instead simply send a empty UDP packet to a high port. The port -this packet is sent to can be configured using the module variable `getmac.PORT`. -- "Fixed" resolution of localhost/127.0.0.1 by hardcoding the response. -This should resolve a lot of problematic edge cases. I'm ok with this -for now since I don't know of a case when it isn't all zeroes. -- Greatly increased the reliability of getting host and interface MACs on Windows -- Improved debugging output -- Tightened up the size of `getmac.py` -- Various minor stability and performance improvements -- Add LICENSE to PyPI package -- Support for Python 3.2 and 3.3. The total downloads from PyPI with -those versions in August was ~53k and ~407K, respectfully. The majority -of those are likely from automated testing (e.g. TravisCI) and not -actual users. Therefore, I've decided to drop support to simplify -development, especially since before 3.4 the 3.x series was still -very much a "work in progress". -- Added automated tests for Windows using Appveyor -- Tox runner for tests -- Added github.io page -- Improved TravisCI testing - -* Thu Aug 30 2018 Christopher Goes 0.3-0 -- Attempt to use Python modules if they're installed. This is useful -for larger projects that already have them installed as dependencies, -as they provide a more reliable means of getting information. -- New methods for remote MACs -- New methods for Interface MACs -- Certain critical failures that should never happen will now warn -instead of failing silently. -- Added a sanity check to the `ip6` argument (IPv6 addresses) -- Improved performance in some areas -- Improved debugging output -- Major Bugfix: search of `proc/net/arp` would return shorter addresses in the -same subnet if they came earlier in the sequence. Example: a search for -`192.168.16.2` on Linux would instead return the MAC address of -`192.168.16.254` with no errors or warning whatsoever. -- Significantly improved default interface detection. Default -interfaces are now properly detected on Linux and most other -POSIX platforms with `ip` or `route` commands available, or the -`netifaces` Python module. -- Makefile -- Vagrantfile to spin up testing VMs for various platforms using [Vagrant](https://www.vagrantup.com/docs/) -- Added more samples of command output on platforms (Ubuntu 18.04 LTS) - -* Sun Aug 26 2018 Christopher Goes 0.2-4 -- Fixed identification of remote host on OSX -- Resolved hangs and noticeable lag that occurred when "network_request" -was True (the default) - - -* Tue Aug 7 2018 Christopher Goes 0.2-3 -- Remote host for Python 3 on Windows - -* Wed Apr 18 2018 Christopher Goes 0.2-2 -- Short versions of CLI arguments (e.g. "-i" for "--interface") -- Improved usage of "ping" across platforms and IP versions -- Various minor tweaks for performance -- Improved Windows detection -- Use of ping command with hostname -- Improvements to internal code - - -* Sun Apr 15 2018 Christopher Goes 0.2-1 -- Nothing changed. PyPI just won't let me push changes without a new version. - -* Sun Apr 15 2018 Christopher Goes 0.2-0 -- Checks for default interface on Linux systems -- New methods of hunting for addresses on Windows, Mac OS X, and Linux -- CLI will output nothing if it failed, instead of "None" -- CLI will return with 1 on failure, 0 on success -- No CLI arguments now implies the default host network interface -- Added an argumnent for debugging: `--debug` -- Removed `-d` option from `--no-network-requests` -- Interfaces on Windows and Linux (including Bash for Windows) -- Many bugs -- Support for Python 2.6 on the CLI -- Overhaul of internals - - -* Sun Apr 15 2018 Christopher Goes 0.1-0: -- Addition of a terminal command: `get-mac` -- Ability to run as a module from the command line: `python -m getmac` -- `arp_request` argument was renamed to `network_request` -- Updated docstring -- Slight reduction in the size of getmac.py -- Overhauled the README -- Moved tests into their own folder -- Added Python 3.7 to list of supported snakes - - -* Sun Nov 12 2017 Christopher Goes 0.0-4: -- Python 2.6 compatibility - - -* Sat Nov 11 2017 Christopher Goes 0.0-3: -- Fixed some addresses returning without colons -- Added more rigorous checks on addresses before returning them - - -* Sat Nov 11 2017 Christopher Goes 0.0-2: -- Remove print statements and other debugging output - - -* Mon Oct 23 2017 Christopher Goes 0.0-1 -- Initial pre-alpha diff --git a/tox.ini b/tox.ini index ad2f4bd..7780b81 100644 --- a/tox.ini +++ b/tox.ini @@ -57,7 +57,7 @@ setenv = deps = -rtests/style-requirements.txt basepython = py311, py310, py3 commands = - codespell --check-filenames --skip="*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,*.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist,*getmac2.1,*getmac.1,./packaging/rpm/man/*" --ignore-words-list "ether,Expresso,expresso,Espresso,espresso" + codespell --check-filenames --skip="*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,*.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist" --ignore-words-list "ether,Expresso,expresso,Espresso,espresso" flake8 getmac tests setup.py bandit --quiet --recursive --skip B404,B603,B104 getmac setup.py vulture --min-confidence 100 getmac tests setup.py From 84a8de6b7a245682ad619ec6be39ea0624eec3a5 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 23 Jan 2023 17:12:44 -0600 Subject: [PATCH 003/112] Fix test --- tests/test_getmac.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_getmac.py b/tests/test_getmac.py index d7284d9..3688465 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -91,7 +91,7 @@ def test_read_file_not_exist(): def test_fetch_ip_using_dns(mocker): - m = mocker.patch("socket.socket") + m = mocker.patch("socket.socket.__enter__") m.return_value.getsockname.return_value = ("1.2.3.4", 51327) assert getmac._fetch_ip_using_dns() == "1.2.3.4" From 1833bc2719d6f8c125b806658172774d153152af Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 23 Jan 2023 18:29:37 -0600 Subject: [PATCH 004/112] fix ci --- .github/workflows/ci.yml | 4 ++-- docs/TODO.md | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ce8b0b..145c462 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,8 +49,8 @@ jobs: - windows-latest - macos-latest exclude: - # - platform: ubuntu-20.04 - # architecture: x86 + - platform: ubuntu-20.04 + architecture: x86 - platform: macos-latest architecture: x86 - platform: macos-latest diff --git a/docs/TODO.md b/docs/TODO.md index 4888637..876a25b 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -7,9 +7,9 @@ - Fix is to check that the flag != 0x0, which should do the trick, unless there's an edge case that it misses. - [ ] Switch to Poetry for project management - [ ] Also, add `isort` -- [ ] Support Python 3.10 and 3.11 - - [ ] Update pytest (pytest 4, which we were using to support python 2.7, doesn't work with python 3.10) - - [ ] add tests + setup.py classifier +- [x] Support Python 3.10 and 3.11 + - [x] Update pytest (pytest 4, which we were using to support python 2.7, doesn't work with python 3.10) + - [x] add tests + setup.py classifier - [ ] **API changes** (technically speaking) - Add argument to `get_mac_address()` to force the platform used (e.g. `platform_override="linux"`) - Also add CLI argument to configure this @@ -32,6 +32,7 @@ - [ ] return data cleanup and validation - [ ] Raise exceptions on critical failures (stuff that were warnings in 0.9.0), all calls to `_warn_critical()`. - [ ] Remove all Python "Scripts" from the path, so they don't interfere with the commands we actually want (e.g. "ping"). Document this behavior! +- [ ] [issue 77](https://github.com/GhostofGoes/getmac/issues/77): Feature: get all mac addresses - [ ] Documentation - [ ] Single page on RTD/publish with GitHub actions built with Sphinx and Furo - [ ] Update docs/usage examples for `get_mac_address()` @@ -80,7 +81,7 @@ ## Py3-related stuff for 1.0.0 - [x] Drop support for python 2.7, 3.4, and 3.5 -- [ ] BUMP TEST DEPENDENCIES AND PYTEST VERSION TO MODERN TIMES (especially pytest...) +- [x] BUMP TEST DEPENDENCIES AND PYTEST VERSION TO MODERN TIMES (especially pytest...) - [ ] Use Enums for platforms and method types instead of strings? - [ ] cache package imports done during test for use during `get()`, reuse - [ ] rewrite strings to f-strings @@ -153,6 +154,7 @@ This is going to be a bit more complicated since the highest metric routes are g - [ ] Add ability to get the mac address of a Python socket's interface (`socket.socket`) - [ ] Test against non-ethernet interfaces (WiFi, LTE, etc.) - [ ] Create a script to collect samples for all relevant commands on a platform and save output into the appropriately named sub-directory in `samples/`. +- [ ] Use [ruff](https://github.com/charliermarsh/ruff) instead of `flake8`? FastAPI, Scikit learn, [PyPA build](https://github.com/pypa/build/pull/565/), and other major projects are moving to it # Post-1.0.0 From 8364ad6b7641087a4694ab16722755ea81f28613 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 23 Jan 2023 18:37:44 -0600 Subject: [PATCH 005/112] 3.12 isn't in CI yet apparently --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 145c462..c467e22 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,6 @@ jobs: - "3.9" - "3.10" - "3.11" - - "3.12" - "pypy-3.6" - "pypy-3.7" - "pypy-3.8" From 7a3dbe2089675b96f557f1ad905c03eaa698b444 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 24 Jan 2023 14:10:30 -0600 Subject: [PATCH 006/112] update copyright year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 1a71c0f..688f69d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Christopher Goes +Copyright (c) 2023 Christopher Goes Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 7b86c358dea9ebce5fad62c13f1db36f45ced770 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 24 Jan 2023 14:11:39 -0600 Subject: [PATCH 007/112] Replace setup.py with pyproject.toml Also, remove building of .deb file. There isn't really any reason to anymore, and stdeb hasn't been updated since 2020. --- .github/workflows/ci.yml | 8 +-- CHANGELOG.md | 1 + docs/TODO.md | 3 +- docs/releasing.md | 31 +++------ getmac/__main__.py | 2 - packaging/Dockerfile | 2 +- pyproject.toml | 131 +++++++++++++++++++++++++++++++++++ setup.py | 90 ------------------------ tests/style-requirements.txt | 2 + tox.ini | 25 ++----- 10 files changed, 157 insertions(+), 138 deletions(-) create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c467e22..5c554ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 with: - python-version: "3.8" + python-version: "3.11" - name: Install tox run: python -m pip install tox - name: Run lint checks @@ -124,10 +124,10 @@ jobs: # uses: "actions/setup-python@v2" # with: # python-version: "${{ matrix.python-version }}" -# - name: "🐍 Install wheel" -# run: "python -m pip install wheel --user" +# - name: "🐍 Install wheel and build" +# run: "python -m pip install wheel build --user" # - name: "🐍 Build a binary wheel and a source tarball" -# run: "python setup.py sdist bdist_wheel" +# run: "python -m build" # - name: "📦 Publish distribution to PyPI" # uses: "pypa/gh-action-pypi-publish@master" # if: "startsWith(github.ref, 'refs/tags')" diff --git a/CHANGELOG.md b/CHANGELOG.md index ef309c7..3a8dee6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ ### Dev * Renamed `master` branch to `main` +* Use `pyproject.toml` instead of `setup.py`. Move most tool configurations to `pyproject.toml` from `tox.ini`. ## 0.9.0 (01/23/2023) **Announcement**: Compatibility with Python versions older than 3.6 (2.7, 3.4, and 3.5) is deprecated and will be removed in getmac 1.0.0. If you are stuck on an unsupported Python, considor loosely pinning the version of this package in your dependency list, e.g. `getmac<1`. diff --git a/docs/TODO.md b/docs/TODO.md index 876a25b..f0d57e0 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -25,6 +25,7 @@ - [ ] Support `ipaddress` objects, `IPv4Address` and `IPv6Address` - [ ] Move method classes into a separate file - [ ] Add new method: `get_default_interface()`. This leverages the default interface detection methods to expose a helpful public API. +- [ ] Add test to ensure only the expected files make it into the sdist and wheel, no unexpected files - [ ] Split utils into a separate file - [ ] move more logic out of `get_mac_address()` into individual methods: - [ ] interface @@ -85,7 +86,7 @@ - [ ] Use Enums for platforms and method types instead of strings? - [ ] cache package imports done during test for use during `get()`, reuse - [ ] rewrite strings to f-strings -- [ ] Use `pyproject.toml` instead of `setup.py` +- [x] Use `pyproject.toml` instead of `setup.py` - https://packaging.python.org/tutorials/packaging-projects/ - Move configurations for tools out of `tox.ini` and into `pyproject.toml` - Add codespell configuration, remove CLI arguments diff --git a/docs/releasing.md b/docs/releasing.md index 6903eab..a38fe69 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -1,42 +1,29 @@ - ## Requirements -**NOTE**: Linux is required to build the `.deb` package. - ```bash python -m pip install -U pip -pip install -U setuptools twine wheel -pip install -U stdeb +python -m pip install -U setuptools twine wheel build ``` ## Cutting a release 1. Increment version number in `getmac/getmac.py` 2. Update CHANGELOG header from UNRELEASED to the version and add the date 3. Run static analysis checks (`tox -e check`) -4. Run the test suite on the main supported platforms (`tox`) - a) Windows - b) Ubuntu - c) CentOS - d) OSX +4. Ensure CI ([GitHub Actions](https://github.com/GhostofGoes/getmac/actions)) is passing on all checks and on all platforms 5. Ensure a pip install from source works on the main platforms: ```bash pip install https://github.com/ghostofgoes/getmac/archive/main.tar.gz ``` -6. Clean the environment: `bash ./scripts/clean.sh` -7. Build the sdist and wheel (`.whl`) +1. Clean the environment: `bash ./scripts/clean.sh` +2. Build the sdist and wheel (`.whl`) ```bash -python setup.py sdist bdist_wheel --universal +python -m build ``` -8. Upload the sdist and wheel (`.whl`) +1. Upload the sdist and wheel (`.whl`) ```bash twine upload dist/* ``` -9. Build the Debian package -```bash -python setup.py --command-packages=stdeb.command bdist_deb -``` -10. Create a tagged release on GitHub including: +1. Create a tagged release on GitHub including: a) The relevant section of the CHANGELOG in the body b) The source and binary wheels - c) The `.deb` package (which will be in `./deb_dist`) -11. Edit the package name in `setup.py` to `get-mac`, and re-run steps 7 and 8 (build and upload), since people apparently don't check their dependencies and ignore runtime warnings. -12. Announce the release in the normal places +2. Edit the package name in `pyproject.toml` to `get-mac`, and re-run steps 7 and 8 (build and upload), since people apparently don't check their dependencies and ignore runtime warnings. +3. Announce the release in the normal places diff --git a/getmac/__main__.py b/getmac/__main__.py index c67c178..613795e 100644 --- a/getmac/__main__.py +++ b/getmac/__main__.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 -from __future__ import print_function - import argparse import logging import sys diff --git a/packaging/Dockerfile b/packaging/Dockerfile index 987ea4a..c98c3f3 100644 --- a/packaging/Dockerfile +++ b/packaging/Dockerfile @@ -6,6 +6,6 @@ LABEL project_url="https://github.com/GhostofGoes/getmac" ADD .. /opt/getmac WORKDIR /opt/getmac -RUN python setup.py install +RUN python -m pip install . ENTRYPOINT ["getmac"] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..73a758c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,131 @@ +[build-system] +requires = ["setuptools>=62.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "getmac" +dynamic = ["version", "readme"] +authors = [ + { name="Christopher Goes", email="ghostofgoes@gmail.com" } +] +description = "Get MAC addresses of remote hosts and local interfaces" +license = { file = "LICENSE" } +requires-python = ">=3.6" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX", + "Operating System :: POSIX :: BSD", + "Operating System :: POSIX :: BSD :: FreeBSD", + "Operating System :: POSIX :: BSD :: NetBSD", + "Operating System :: POSIX :: BSD :: OpenBSD", + "Operating System :: POSIX :: Linux", + "Operating System :: POSIX :: Other", + "Operating System :: MacOS", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "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", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + "Intended Audience :: Information Technology", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: Libraries", + "Topic :: System :: Systems Administration", + "Topic :: System :: Networking", + "Topic :: Utilities", + "Typing :: Typed" +] +keywords=[ + "getmac", + "get-mac", + "macaddress", + "mac-address", + "mac", + "ethernet", + "mac-48", + "networking", + "network", + "networking", + "layer2", + "layer-2", + "802.3" +] + +[project.urls] +"Homepage" = "https://github.com/GhostofGoes/getmac" +# TODO: get ReadTheDocs working +# "Documentation" = "https://getmac.readthedocs.io/en/latest/" +"Changelog" = "https://github.com/GhostofGoes/getmac/blob/main/CHANGELOG.md" +"Issue tracker" = "https://github.com/GhostofGoes/getmac/issues" +"Source" = "https://github.com/GhostofGoes/getmac" +"Discord server" = "https://discord.gg/python" + +[project.scripts] +getmac = "getmac.__main__:main" + +# TODO: move from requirement files to optional deps managed by poetry +# [project.optional-dependencies] +# tests = [ +# "coverage[toml]", +# "pytest>=6.0", +# ... +# ] +# docs = [ +# "furo", +# "myst-parser", +# "sphinx", +# "sphinx-notfound-page", +# "sphinxcontrib-mermaid", +# ... +# ] +# dev = [ +# "pre-commit", +# ... +# ] + + +[tool.setuptools] +platforms = ["any"] +zip-safe = true +packages = ["getmac"] + +[tool.setuptools.dynamic] +version = { attr = "getmac.getmac.__version__" } +# Build the page that will be displayed on PyPI from the README and CHANGELOG +readme = { file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown" } + + +[tool.pytest.ini_options] +testpaths = "tests" +norecursedirs = ".github getmac samples scripts .git .tox .vagrant .idea .vscode dist build *.egg .* .venv" +addopts = "--tb=short" +# filterwarnings = [ +# "ignore::DeprecationWarning" +# ] + +[tool.coverage.run] +relative_files = true + +[tool.codespell] +check-filenames = true +skip = '*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,*.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist' +ignore-words-list = 'ether,Expresso,expresso,Espresso,espresso' + +[tool.bandit] +recursive = true +targets = ["getmac"] +skips = ["B404", "B603", "B104"] + +[tool.black] +target-version = ["py36", "py37", "py38", "py39", "py310", "py311"] diff --git a/setup.py b/setup.py deleted file mode 100644 index 7940ff1..0000000 --- a/setup.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env python3 - -from setuptools import setup - -from getmac.getmac import __version__ - - -# Build the page that will be displayed on PyPI from the README and CHANGELOG -with open("README.md", encoding="utf-8") as f: - long_description = f.read() -long_description += "\n\n" -with open("CHANGELOG.md", encoding="utf-8") as f: - long_description += f.read() - - -setup( - name="getmac", - version=__version__, - author="Christopher Goes", - author_email="ghostofgoes@gmail.com", - description="Get MAC addresses of remote hosts and local interfaces", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/GhostofGoes/getmac", - project_urls={ - "Documentation": "https://getmac.readthedocs.io/en/latest/", - "Changelog": "https://github.com/GhostofGoes/getmac/blob/main/CHANGELOG.md", - "Issue tracker": "https://github.com/GhostofGoes/getmac/issues", - "Source": "https://github.com/GhostofGoes/getmac", - "Discord server": "https://discord.gg/python", - }, - license="MIT", - data_files=[], - packages=["getmac"], - zip_safe=True, - entry_points={"console_scripts": ["getmac = getmac.__main__:main"]}, - install_requires=[], - python_requires=">=3.6", - keywords=[ - "getmac", - "get-mac", - "macaddress", - "mac-address", - "mac", - "ethernet", - "mac-48", - "networking", - "network", - "networking", - "layer2", - "layer-2", - "802.3", - ], - classifiers=[ - # TODO(1.0.0): change to 5 - Production/Stable - "Development Status :: 4 - Beta", - "Environment :: Console", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Operating System :: Microsoft :: Windows", - "Operating System :: POSIX", - "Operating System :: POSIX :: BSD", - "Operating System :: POSIX :: BSD :: FreeBSD", - "Operating System :: POSIX :: BSD :: NetBSD", - "Operating System :: POSIX :: BSD :: OpenBSD", - "Operating System :: POSIX :: Linux", - "Operating System :: POSIX :: Other", - "Operating System :: MacOS", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "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", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Intended Audience :: Developers", - "Intended Audience :: System Administrators", - "Intended Audience :: Information Technology", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Software Development :: Libraries", - "Topic :: System :: Systems Administration", - "Topic :: System :: Networking", - "Topic :: Utilities", - "Typing :: Typed", - ], -) diff --git a/tests/style-requirements.txt b/tests/style-requirements.txt index 4d88e8e..a79fd6d 100644 --- a/tests/style-requirements.txt +++ b/tests/style-requirements.txt @@ -1,5 +1,6 @@ check-manifest==0.49 codespell==2.2.2 +tomli flake8>=5.0.0 flake8-bugbear==23.1.20;python_version>='3.5' flake8-import-order==0.18.2 @@ -23,3 +24,4 @@ https://github.com/ambv/flake8-mypy/archive/master.tar.gz bandit>=1.6.0 black==22.12.0;python_version>='3.6' vulture==2.7 +validate-pyproject==0.11 diff --git a/tox.ini b/tox.ini index 7780b81..6b6c4a6 100644 --- a/tox.ini +++ b/tox.ini @@ -45,23 +45,23 @@ commands = -n auto {posargs} [testenv:benchmark] -description = "Run the test suite with benchmarks enabled" +description = Run the test suite with benchmarks enabled commands = pytest --basetemp={envtmpdir} --benchmark-enable -n auto {posargs} [testenv:check] -description = "Run code quality, static analysis, and spelling checks" +description = Run code quality, static analysis, and spelling checks setenv = PIP_DISABLE_PIP_VERSION_CHECK = 1 MYPYPATH=getmac deps = -rtests/style-requirements.txt basepython = py311, py310, py3 commands = - codespell --check-filenames --skip="*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,*.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist" --ignore-words-list "ether,Expresso,expresso,Espresso,espresso" - flake8 getmac tests setup.py - bandit --quiet --recursive --skip B404,B603,B104 getmac setup.py - vulture --min-confidence 100 getmac tests setup.py - python setup.py --quiet check --strict --metadata + codespell + flake8 getmac tests + bandit -q -r -c {toxinidir}/pyproject.toml getmac + vulture --min-confidence 100 getmac tests + validate-pyproject {toxinidir}/pyproject.toml check-manifest --quiet {toxinidir} [flake8] @@ -74,14 +74,3 @@ per-file-ignores = import-order-style = pycharm show-source = True max-complexity = 38 - -[pytest] -testpaths = tests -norecursedirs = .github getmac samples scripts .git .tox .vagrant .idea .vscode dist build *.egg .* .venv -addopts = - --tb=short -filterwarnings = - ignore::DeprecationWarning - -[coverage:run] -relative_files = True From 2cf693fcfde59a59b05911b4bcb1811fc802b6d1 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 24 Jan 2023 15:03:24 -0600 Subject: [PATCH 008/112] fix tox version --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c554ba..b1b17ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: with: python-version: "3.11" - name: Install tox - run: python -m pip install tox + run: python -m pip install 'tox>4' - name: Run lint checks run: tox -e check test: @@ -74,7 +74,7 @@ jobs: python -c "import os; print('os.name: %s' % str(os.name))" python -c "import platform; print('platform.uname(): %s' % str(platform.uname()))" - name: Install dependencies - run: python -m pip install tox coveralls tox-gh-actions + run: python -m pip install 'tox>4' coveralls tox-gh-actions # TODO: fix benchmark results (maybe a separate Job?) - name: Run tests run: tox From 69f1a44104d1509f92181ad0cf79668fb29b5fe6 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 24 Jan 2023 16:46:29 -0600 Subject: [PATCH 009/112] Drop Python 3.6 support --- .github/workflows/ci.yml | 4 ---- CHANGELOG.md | 6 ++++-- README.md | 2 +- docs/TODO.md | 2 +- pyproject.toml | 3 +-- tests/style-requirements.txt | 10 +++++----- tox.ini | 4 +--- 7 files changed, 13 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1b17ee..0635ece 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,13 +30,11 @@ jobs: # OS images: https://github.com/actions/runner-images matrix: python-version: - - "3.6" - "3.7" - "3.8" - "3.9" - "3.10" - "3.11" - - "pypy-3.6" - "pypy-3.7" - "pypy-3.8" - "pypy-3.9" @@ -52,8 +50,6 @@ jobs: architecture: x86 - platform: macos-latest architecture: x86 - - platform: macos-latest - python-version: "pypy-3.6" - platform: macos-latest python-version: "pypy-3.7" - platform: macos-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 7293144..c980009 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ## UNRELEASED ### Added -* Added support for Python 3.10 and 3.11 +* Added full support for Python 3.10 and 3.11 * Added tentative support for Python 3.12 ### Changed @@ -13,7 +13,8 @@ ### Removed * Removed support for Python 2.7. It's time. Supporting 2.7 has become an onerous burden on the project, and has prevented fully supporting 3.10 and 3.11 due to having to stay on an ancient version of pytest. I'm done supporting a 13 year old version of a language. -* Removed support for Python 3.4 and 3.5. These are quite old and there were a number of essential features added in 3.6 that I'd like to use (f-strings, type annotations, mature `pathlib`, etc.) +* Removed support for Python 3.4 and 3.5. These are quite old and there were a number of essential features added in 3.6+ that I'd like to use (f-strings, type annotations, mature `pathlib`, etc.) +* Removed support for Python 3.6. setuptools added pyproject.toml support in version 61.0.0, however only version 59 is supported for Python 3.6. This means a choice had to be made between moving to pyproject.toml or supporting Python 3.6. Given 3.6 has been EOL for over a year, I've decided to drop support. * Removed support for Jython. As of Jan 23rd, 2023, [Jython](https://github.com/jython/jython) still does not support Python3, only 2.7. If and when it supports Python 3, I'll re-add support for it. * Removed explicit support for IronPython. [IronPython3 exists](https://github.com/IronLanguages/ironpython3), however I don't have a way to test it in CI. If someone knows of a way to test it in GitHub actions, let me know, and I'm happy to re-add explicit support. * Removed RPM packaging. This wasn't being used anywhere, and hasn't been updated since version 0.6.0. Way back when, I dreamt of putting this in OS package repositories. However, I've now learned that's up to the maintainers of those repos (e.g., Ubuntu or Fedora maintainers), not me. @@ -21,6 +22,7 @@ ### Dev * Renamed `master` branch to `main` * Use `pyproject.toml` instead of `setup.py`. Move most tool configurations to `pyproject.toml` from `tox.ini`. + ## 0.9.1 (01/24/2023) **Announcement**: Compatibility with Python versions older than 3.7 (2.7, 3.4, 3.5, and 3.6) is deprecated and will be removed in getmac 1.0.0. If you are stuck on an unsupported Python, consider loosely pinning the version of this package in your dependency list, e.g. `getmac<1.0.0` or `getmac~=0.9.0`. diff --git a/README.md b/README.md index 150b91f..c9c76dc 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost ## Features - Pure-Python (no compiled C-extensions required!) -- Python 3.6+ +- Python 3.7+ - Lightweight, with no dependencies and a relatively small package size (no binary extensions) - Supports most platforms with NO binary extensions required! - Supports CPython and [PyPy](https://www.pypy.org/) diff --git a/docs/TODO.md b/docs/TODO.md index f0d57e0..8769d9a 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -79,11 +79,11 @@ - [ ] finer-grained platform support identification for methods by versions/releases, e.g. Windows 7 vs 10, Ubuntu 12 vs 20 - [ ] CLI: put "override" and other debugging-related arguments into a separate argparse argument group - [ ] Refactor to build a local state of the interfaces on the system, and use that as fallback for default lookup of interface with no name. Could also include MACs for faster lookup of future interface queries. Similar to how `netifaces` works, with a dict with interface infos. Properly address https://github.com/GhostofGoes/getmac/issues/78 +- [ ] Replace `flake8-mypy` with proper execution of mypy in tests (the project is dead and archived, https://github.com/ambv/flake8-mypy) ## Py3-related stuff for 1.0.0 - [x] Drop support for python 2.7, 3.4, and 3.5 - [x] BUMP TEST DEPENDENCIES AND PYTEST VERSION TO MODERN TIMES (especially pytest...) -- [ ] Use Enums for platforms and method types instead of strings? - [ ] cache package imports done during test for use during `get()`, reuse - [ ] rewrite strings to f-strings - [x] Use `pyproject.toml` instead of `setup.py` diff --git a/pyproject.toml b/pyproject.toml index 73a758c..c75b2da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ authors = [ ] description = "Get MAC addresses of remote hosts and local interfaces" license = { file = "LICENSE" } -requires-python = ">=3.6" +requires-python = ">=3.7" classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", @@ -27,7 +27,6 @@ classifiers = [ "Operating System :: MacOS", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", diff --git a/tests/style-requirements.txt b/tests/style-requirements.txt index a79fd6d..c1d88e4 100644 --- a/tests/style-requirements.txt +++ b/tests/style-requirements.txt @@ -2,7 +2,7 @@ check-manifest==0.49 codespell==2.2.2 tomli flake8>=5.0.0 -flake8-bugbear==23.1.20;python_version>='3.5' +flake8-bugbear==23.1.20 flake8-import-order==0.18.2 flake8-tidy-imports==4.8.0 flake8-print==5.0.0 @@ -15,13 +15,13 @@ flake8-string-format==0.3.0 flake8-broken-line==0.6.0 flake8-mutable==1.2.0 flake8-pytest-style==1.6.0 -# flake8-annotations 3.0.0 removes support for PEP-484 type comments +# TODO: flake8-annotations 3.0.0 removes support for PEP-484 type comments flake8-annotations<3.0.0 -flake8-black;python_version>='3.6' +flake8-black +# TODO: replace flake8-mypy with proper execution of mypy # NOTE: the released version of flake8-mypy is buggy on Windows (17.8.0) -# flake8-mypy;python_version>='3.5' https://github.com/ambv/flake8-mypy/archive/master.tar.gz bandit>=1.6.0 -black==22.12.0;python_version>='3.6' +black==22.12.0 vulture==2.7 validate-pyproject==0.11 diff --git a/tox.ini b/tox.ini index 6b6c4a6..186a4c7 100644 --- a/tox.ini +++ b/tox.ini @@ -1,17 +1,15 @@ [tox] -envlist = py{36,37,38,39,310,311,312,py3}-{linux,macos,windows} +envlist = py{37,38,39,310,311,312,py3}-{linux,macos,windows} skip_missing_interpreters = true [gh-actions] python = - 3.6: py36 3.7: py37 3.8: py38 3.9: py39 3.10: py310 3.11: py311 3.12: py312 - pypy-3.6: pypy3 pypy-3.7: pypy3 pypy-3.8: pypy3 pypy-3.9: pypy3 From 00d47f9a3d626d33b9ad919b35e08ce014063214 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 24 Jan 2023 17:17:28 -0600 Subject: [PATCH 010/112] attempt to fix ci --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0635ece..e34bfe4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: python -c "import os; print('os.name: %s' % str(os.name))" python -c "import platform; print('platform.uname(): %s' % str(platform.uname()))" - name: Install dependencies - run: python -m pip install 'tox>4' coveralls tox-gh-actions + run: python -m pip install 'tox>4' coveralls tox-gh-actions tomli # TODO: fix benchmark results (maybe a separate Job?) - name: Run tests run: tox From 159dd37bf734a6347a3e8e5e234895b93d0cee84 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 25 Jan 2023 15:01:44 -0600 Subject: [PATCH 011/112] Move to Poetry --- .github/workflows/ci.yml | 34 +- CHANGELOG.md | 10 +- MANIFEST.in | 15 - docs/TODO.md | 6 +- docs/releasing.md | 26 +- docs/requirements.txt | 3 - poetry.lock | 2001 ++++++++++++++++++++++++++++++++++ pyproject.toml | 180 ++- tests/style-requirements.txt | 27 - tests/test-requirements.txt | 7 - tox.ini | 35 +- 11 files changed, 2218 insertions(+), 126 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 docs/requirements.txt create mode 100644 poetry.lock delete mode 100644 tests/style-requirements.txt delete mode 100644 tests/test-requirements.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e34bfe4..d5e9283 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,13 +11,16 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - name: Install Python + uses: actions/setup-python@v4 with: python-version: "3.11" - - name: Install tox - run: python -m pip install 'tox>4' + - name: Install Poetry + uses: snok/install-poetry@v1 + - name: Install dependencies (poetry) + run: poetry install --only=main,lint --no-interaction - name: Run lint checks - run: tox -e check + run: poetry run tox -e check test: name: Run tests runs-on: ${{ matrix.platform }} @@ -35,9 +38,9 @@ jobs: - "3.9" - "3.10" - "3.11" - - "pypy-3.7" - - "pypy-3.8" - - "pypy-3.9" + - "pypy3.7" + - "pypy3.8" + - "pypy3.9" architecture: - x86 - x64 @@ -51,17 +54,20 @@ jobs: - platform: macos-latest architecture: x86 - platform: macos-latest - python-version: "pypy-3.7" + python-version: "pypy3.7" - platform: macos-latest - python-version: "pypy-3.8" + python-version: "pypy3.8" - platform: macos-latest - python-version: "pypy-3.9" + python-version: "pypy3.9" steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - name: Install Python + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} + - name: Install Poetry + uses: snok/install-poetry@v1 - name: Display platform and environment information run: | python --version @@ -69,11 +75,11 @@ jobs: python -c "import sys; print('sys.platform: %s' % str(sys.platform))" python -c "import os; print('os.name: %s' % str(os.name))" python -c "import platform; print('platform.uname(): %s' % str(platform.uname()))" - - name: Install dependencies - run: python -m pip install 'tox>4' coveralls tox-gh-actions tomli + - name: Install dependencies (poetry) + run: poetry install --no-interaction # TODO: fix benchmark results (maybe a separate Job?) - name: Run tests - run: tox + run: poetry run tox - uses: codecov/codecov-action@v2 with: fail_ci_if_error: true diff --git a/CHANGELOG.md b/CHANGELOG.md index c980009..ea5a588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ + # Changelog **NOTE**: if any changes significantly impact your project or use case, please open an issue on [GitHub](https://github.com/GhostofGoes/getmac/issues) or email me (see git commit author info for address). @@ -9,7 +10,7 @@ * Added tentative support for Python 3.12 ### Changed -* TBD +* Reduce wheel file size (`.whl`) ### Removed * Removed support for Python 2.7. It's time. Supporting 2.7 has become an onerous burden on the project, and has prevented fully supporting 3.10 and 3.11 due to having to stay on an ancient version of pytest. I'm done supporting a 13 year old version of a language. @@ -21,7 +22,12 @@ ### Dev * Renamed `master` branch to `main` -* Use `pyproject.toml` instead of `setup.py`. Move most tool configurations to `pyproject.toml` from `tox.ini`. +* Switch to the excellent and elegant [Poetry](https://python-poetry.org/) for project management + * Add `pyproject.toml` + * Move most tool configurations to `pyproject.toml` from `tox.ini` + * Remove all `requirement.txt` files + * Remove `setup.py` and `MANIFEST.in` + ## 0.9.1 (01/24/2023) **Announcement**: Compatibility with Python versions older than 3.7 (2.7, 3.4, 3.5, and 3.6) is deprecated and will be removed in getmac 1.0.0. If you are stuck on an unsupported Python, consider loosely pinning the version of this package in your dependency list, e.g. `getmac<1.0.0` or `getmac~=0.9.0`. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 0afa6e5..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,15 +0,0 @@ -include LICENSE README.md CHANGELOG.md CONTRIBUTING.md - -graft getmac -graft tests - -prune docs -prune .github -prune packaging -prune scripts - -global-exclude *.py[cod] __pycache__ *.so *.class -exclude *-getmac.spec *.yml -exclude tox.ini Dockerfile Vagrantfile .editorconfig -exclude conf.py index.md make.bat Makefile rewrite.md releasing.md -exclude style-requirements.txt requirements.txt TODO.md CODE_OF_CONDUCT.md SECURITY.md diff --git a/docs/TODO.md b/docs/TODO.md index 8769d9a..c9c4452 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -5,7 +5,7 @@ - [ ] [issue #76](https://github.com/GhostofGoes/getmac/issues/76): get_mac_address() is caching an old mac address, no longer present in local ARP - get_mac_address() is caching an old mac address for a given IP, even when it has timeout from OS ARP table. Only an explicit delete of the ARP entry on the OS make it return '00:00:00:00:00:00' again. - Fix is to check that the flag != 0x0, which should do the trick, unless there's an edge case that it misses. -- [ ] Switch to Poetry for project management +- [x] Switch to Poetry for project management - [ ] Also, add `isort` - [x] Support Python 3.10 and 3.11 - [x] Update pytest (pytest 4, which we were using to support python 2.7, doesn't work with python 3.10) @@ -80,6 +80,9 @@ - [ ] CLI: put "override" and other debugging-related arguments into a separate argparse argument group - [ ] Refactor to build a local state of the interfaces on the system, and use that as fallback for default lookup of interface with no name. Could also include MACs for faster lookup of future interface queries. Similar to how `netifaces` works, with a dict with interface infos. Properly address https://github.com/GhostofGoes/getmac/issues/78 - [ ] Replace `flake8-mypy` with proper execution of mypy in tests (the project is dead and archived, https://github.com/ambv/flake8-mypy) +- [ ] Add a deprecation warning to `get-mac` package, don't publish it for 1.0.0 +- [ ] Add all project contributors to list of Authors in package metadata in `pyproject.toml` (and the documentation) +- [ ] Update supported versions table in [SECURITY.md](../SECURITY.md) ## Py3-related stuff for 1.0.0 - [x] Drop support for python 2.7, 3.4, and 3.5 @@ -170,7 +173,6 @@ This is going to be a bit more complicated since the highest metric routes are g - [ ] Cache method checks (maybe move this to 1.1.0 release?) Save a string with the names of methods. Save to: file (location configurable via environment variable or option). Read from: file, environment variable, file pointed to by environment variable. Add a flag to control this behavior and location of the cache. Document the behavior. - [ ] Add a "net_ok" argument, check network_request attribute on method in CACHE, if not then keep checking for method in FALLBACK_CACHE that has network_request. - [ ] Add ability to specify what methods to use via function argument and CLI argument - - [ ] Function arguments - [ ] Add ability to force platform name (e.g. `linux`) via function argument and CLI argument ``` methods=None diff --git a/docs/releasing.md b/docs/releasing.md index a38fe69..825ed9d 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -1,29 +1,25 @@ ## Requirements -```bash -python -m pip install -U pip -python -m pip install -U setuptools twine wheel build -``` +- Poetry: https://python-poetry.org/docs/#installing-with-the-official-installer +- Configured `~/.pypirc` file with a token for `getmac` (for publishing to PyPI) ## Cutting a release 1. Increment version number in `getmac/getmac.py` -2. Update CHANGELOG header from UNRELEASED to the version and add the date -3. Run static analysis checks (`tox -e check`) -4. Ensure CI ([GitHub Actions](https://github.com/GhostofGoes/getmac/actions)) is passing on all checks and on all platforms -5. Ensure a pip install from source works on the main platforms: +1. Update CHANGELOG header from UNRELEASED to the version and add the date +1. Run static analysis checks (`tox -e check`) +1. Ensure CI ([GitHub Actions](https://github.com/GhostofGoes/getmac/actions)) is passing on all checks and on all platforms +1. Ensure a pip install from source works on the main platforms: ```bash pip install https://github.com/ghostofgoes/getmac/archive/main.tar.gz ``` 1. Clean the environment: `bash ./scripts/clean.sh` -2. Build the sdist and wheel (`.whl`) +1. Build the sdist (`.tar.gz`) and wheel (`.whl`) ```bash -python -m build +poetry build ``` -1. Upload the sdist and wheel (`.whl`) +1. Upload the sdist (`.tar.gz`) and wheel (`.whl`) to PyPI ```bash -twine upload dist/* +poetry publish ``` -1. Create a tagged release on GitHub including: +1. Create a tagged release on GitHub including: a) The relevant section of the CHANGELOG in the body b) The source and binary wheels -2. Edit the package name in `pyproject.toml` to `get-mac`, and re-run steps 7 and 8 (build and upload), since people apparently don't check their dependencies and ignore runtime warnings. -3. Announce the release in the normal places diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index db6ef10..0000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -sphinx>=1.7 -recommonmark -sphinx_rtd_theme \ No newline at end of file diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..3cc770b --- /dev/null +++ b/poetry.lock @@ -0,0 +1,2001 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + +[[package]] +name = "alabaster" +version = "0.7.13" +description = "A configurable sidebar-enabled Sphinx theme" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, + {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, +] + +[[package]] +name = "attrs" +version = "22.2.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] +tests = ["attrs[tests-no-zope]", "zope.interface"] +tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] + +[[package]] +name = "babel" +version = "2.11.0" +description = "Internationalization utilities" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"}, + {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"}, +] + +[package.dependencies] +pytz = ">=2015.7" + +[[package]] +name = "bandit" +version = "1.7.4" +description = "Security oriented static analyser for python code." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"}, + {file = "bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +GitPython = ">=1.0.1" +PyYAML = ">=5.3.1" +stevedore = ">=1.20.0" + +[package.extras] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "toml"] +toml = ["toml"] +yaml = ["PyYAML"] + +[[package]] +name = "beautifulsoup4" +version = "4.11.1" +description = "Screen-scraping library" +category = "dev" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, + {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "black" +version = "22.12.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, + {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, + {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, + {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, + {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, + {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, + {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, + {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, + {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, + {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, + {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, + {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "build" +version = "0.10.0" +description = "A simple, correct Python build frontend" +category = "dev" +optional = false +python-versions = ">= 3.7" +files = [ + {file = "build-0.10.0-py3-none-any.whl", hash = "sha256:af266720050a66c893a6096a2f410989eeac74ff9a68ba194b3f6473e8e26171"}, + {file = "build-0.10.0.tar.gz", hash = "sha256:d5b71264afdb5951d6704482aac78de887c80691c52b88a9ad195983ca2c9269"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "os_name == \"nt\""} +importlib-metadata = {version = ">=0.22", markers = "python_version < \"3.8\""} +packaging = ">=19.0" +pyproject_hooks = "*" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2021.08.31)", "sphinx (>=4.0,<5.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)"] +test = ["filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "toml (>=0.10.0)", "wheel (>=0.36.0)"] +typing = ["importlib-metadata (>=5.1)", "mypy (==0.991)", "tomli", "typing-extensions (>=3.7.4.3)"] +virtualenv = ["virtualenv (>=20.0.35)"] + +[[package]] +name = "cachetools" +version = "5.3.0" +description = "Extensible memoizing collections and decorators" +category = "dev" +optional = false +python-versions = "~=3.7" +files = [ + {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, + {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, +] + +[[package]] +name = "certifi" +version = "2022.12.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] + +[[package]] +name = "chardet" +version = "5.1.0" +description = "Universal encoding detector for Python 3" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "chardet-5.1.0-py3-none-any.whl", hash = "sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"}, + {file = "chardet-5.1.0.tar.gz", hash = "sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.0.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "charset-normalizer-3.0.1.tar.gz", hash = "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-win32.whl", hash = "sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b"}, + {file = "charset_normalizer-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-win32.whl", hash = "sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3"}, + {file = "charset_normalizer-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-win32.whl", hash = "sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41"}, + {file = "charset_normalizer-3.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154"}, + {file = "charset_normalizer-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-win32.whl", hash = "sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e"}, + {file = "charset_normalizer-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-win32.whl", hash = "sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8"}, + {file = "charset_normalizer-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59"}, + {file = "charset_normalizer-3.0.1-py3-none-any.whl", hash = "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24"}, +] + +[[package]] +name = "check-manifest" +version = "0.49" +description = "Check MANIFEST.in in a Python source package for completeness" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "check-manifest-0.49.tar.gz", hash = "sha256:64a640445542cf226919657c7b78d02d9c1ca5b1c25d7e66e0e1ff325060f416"}, + {file = "check_manifest-0.49-py3-none-any.whl", hash = "sha256:058cd30057714c39b96ce4d83f254fc770e3145c7b1932b5940b4e3efb5521ef"}, +] + +[package.dependencies] +build = ">=0.1" +setuptools = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[package.extras] +test = ["mock (>=3.0.0)", "pytest"] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "codespell" +version = "2.2.2" +description = "Codespell" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "codespell-2.2.2-py3-none-any.whl", hash = "sha256:87dfcd9bdc9b3cb8b067b37f0af22044d7a84e28174adfc8eaa203056b7f9ecc"}, + {file = "codespell-2.2.2.tar.gz", hash = "sha256:c4d00c02b5a2a55661f00d5b4b3b5a710fa803ced9a9d7e45438268b099c319c"}, +] + +[package.extras] +dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency", "tomli"] +hard-encoding-detection = ["chardet"] +toml = ["tomli"] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "commonmark" +version = "0.9.1" +description = "Python parser for the CommonMark Markdown spec" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, + {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, +] + +[package.extras] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] + +[[package]] +name = "coverage" +version = "6.5.0" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, + {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, + {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, + {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, + {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, + {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "coveralls" +version = "3.3.1" +description = "Show coverage stats online via coveralls.io" +category = "dev" +optional = false +python-versions = ">= 3.5" +files = [ + {file = "coveralls-3.3.1-py2.py3-none-any.whl", hash = "sha256:f42015f31d386b351d4226389b387ae173207058832fbf5c8ec4b40e27b16026"}, + {file = "coveralls-3.3.1.tar.gz", hash = "sha256:b32a8bb5d2df585207c119d6c01567b81fba690c9c10a753bfe27a335bfc43ea"}, +] + +[package.dependencies] +coverage = ">=4.1,<6.0.0 || >6.1,<6.1.1 || >6.1.1,<7.0" +docopt = ">=0.6.1" +requests = ">=1.0.0" + +[package.extras] +yaml = ["PyYAML (>=3.10)"] + +[[package]] +name = "distlib" +version = "0.3.6" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] + +[[package]] +name = "docopt" +version = "0.6.2" +description = "Pythonic argument parser, that will make you smile" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, +] + +[[package]] +name = "docutils" +version = "0.18.1" +description = "Docutils -- Python Documentation Utilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"}, + {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.0" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"}, + {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "execnet" +version = "1.9.0" +description = "execnet: rapid multi-Python deployment" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, + {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, +] + +[package.extras] +testing = ["pre-commit"] + +[[package]] +name = "fastjsonschema" +version = "2.16.2" +description = "Fastest Python implementation of JSON schema" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "fastjsonschema-2.16.2-py3-none-any.whl", hash = "sha256:21f918e8d9a1a4ba9c22e09574ba72267a6762d47822db9add95f6454e51cc1c"}, + {file = "fastjsonschema-2.16.2.tar.gz", hash = "sha256:01e366f25d9047816fe3d288cbfc3e10541daf0af2044763f3d0ade42476da18"}, +] + +[package.extras] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] + +[[package]] +name = "filelock" +version = "3.9.0" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.9.0-py3-none-any.whl", hash = "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"}, + {file = "filelock-3.9.0.tar.gz", hash = "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de"}, +] + +[package.extras] +docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "flake8" +version = "6.0.0" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-6.0.0-py2.py3-none-any.whl", hash = "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7"}, + {file = "flake8-6.0.0.tar.gz", hash = "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.10.0,<2.11.0" +pyflakes = ">=3.0.0,<3.1.0" + +[[package]] +name = "flake8-annotations" +version = "2.9.1" +description = "Flake8 Type Annotation Checks" +category = "dev" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "flake8-annotations-2.9.1.tar.gz", hash = "sha256:11f09efb99ae63c8f9d6b492b75fe147fbc323179fddfe00b2e56eefeca42f57"}, + {file = "flake8_annotations-2.9.1-py3-none-any.whl", hash = "sha256:a4385158a7a9fc8af1d8820a2f4c8d03387997006a83f5f8bfe5bc6085bdf88a"}, +] + +[package.dependencies] +attrs = ">=21.4" +flake8 = ">=3.7" +typed-ast = {version = ">=1.4,<2.0", markers = "python_version < \"3.8\""} + +[[package]] +name = "flake8-black" +version = "0.3.6" +description = "flake8 plugin to call black as a code style validator" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-black-0.3.6.tar.gz", hash = "sha256:0dfbca3274777792a5bcb2af887a4cad72c72d0e86c94e08e3a3de151bb41c34"}, + {file = "flake8_black-0.3.6-py3-none-any.whl", hash = "sha256:fe8ea2eca98d8a504f22040d9117347f6b367458366952862ac3586e7d4eeaca"}, +] + +[package.dependencies] +black = ">=22.1.0" +flake8 = ">=3" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[package.extras] +develop = ["build", "twine"] + +[[package]] +name = "flake8-blind-except" +version = "0.2.1" +description = "A flake8 extension that checks for blind except: statements" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, +] + +[[package]] +name = "flake8-bugbear" +version = "23.1.20" +description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-bugbear-23.1.20.tar.gz", hash = "sha256:55902ab5a48c5ea53d8689ecd146eda548e72f2724192b9c1d68f6d975d13c06"}, + {file = "flake8_bugbear-23.1.20-py3-none-any.whl", hash = "sha256:04a115e5f9c8e87c38bdbbcdf9f58223ffe05469c07c9a7bd8633330bc4d078b"}, +] + +[package.dependencies] +attrs = ">=19.2.0" +flake8 = ">=3.0.0" + +[package.extras] +dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] + +[[package]] +name = "flake8-builtins" +version = "2.1.0" +description = "Check for python builtins being used as variables or parameters." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-builtins-2.1.0.tar.gz", hash = "sha256:12ff1ee96dd4e1f3141141ee6c45a5c7d3b3c440d0949e9b8d345c42b39c51d4"}, + {file = "flake8_builtins-2.1.0-py3-none-any.whl", hash = "sha256:469e8f03d6d0edf4b1e62b6d5a97dce4598592c8a13ec8f0952e7a185eba50a1"}, +] + +[package.dependencies] +flake8 = "*" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "flake8-comprehensions" +version = "3.10.1" +description = "A flake8 plugin to help you write better list/set/dict comprehensions." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-comprehensions-3.10.1.tar.gz", hash = "sha256:412052ac4a947f36b891143430fef4859705af11b2572fbb689f90d372cf26ab"}, + {file = "flake8_comprehensions-3.10.1-py3-none-any.whl", hash = "sha256:d763de3c74bc18a79c039a7ec732e0a1985b0c79309ceb51e56401ad0a2cd44e"}, +] + +[package.dependencies] +flake8 = ">=3.0,<3.2.0 || >3.2.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "flake8-debugger" +version = "4.1.2" +description = "ipdb/pdb statement checker plugin for flake8" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, + {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-import-order" +version = "0.18.2" +description = "Flake8 and pylama plugin that checks the ordering of import statements." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "flake8-import-order-0.18.2.tar.gz", hash = "sha256:e23941f892da3e0c09d711babbb0c73bc735242e9b216b726616758a920d900e"}, + {file = "flake8_import_order-0.18.2-py2.py3-none-any.whl", hash = "sha256:82ed59f1083b629b030ee9d3928d9e06b6213eb196fe745b3a7d4af2168130df"}, +] + +[package.dependencies] +pycodestyle = "*" +setuptools = "*" + +[[package]] +name = "flake8-mutable" +version = "1.2.0" +description = "mutable defaults flake8 extension" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "flake8-mutable-1.2.0.tar.gz", hash = "sha256:ee9b77111b867d845177bbc289d87d541445ffcc6029a0c5c65865b42b18c6a6"}, + {file = "flake8_mutable-1.2.0-py2-none-any.whl", hash = "sha256:38fd9dadcbcda6550a916197bc40ed76908119dabb37fbcca30873666c31d2d5"}, +] + +[package.dependencies] +flake8 = "*" + +[[package]] +name = "flake8-mypy" +version = "17.8.0" +description = "A plugin for flake8 integrating mypy." +category = "dev" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +attrs = "*" +flake8 = ">=3.0.0" +mypy = "*" + +[package.source] +type = "git" +url = "https://github.com/ambv/flake8-mypy.git" +reference = "HEAD" +resolved_reference = "2b00b0133656af4da468a3d24935df71ffc34722" + +[[package]] +name = "flake8-plugin-utils" +version = "1.3.2" +description = "The package provides base classes and utils for flake8 plugin writing" +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" +files = [ + {file = "flake8-plugin-utils-1.3.2.tar.gz", hash = "sha256:20fa2a8ca2decac50116edb42e6af0a1253ef639ad79941249b840531889c65a"}, + {file = "flake8_plugin_utils-1.3.2-py3-none-any.whl", hash = "sha256:1fe43e3e9acf3a7c0f6b88f5338cad37044d2f156c43cb6b080b5f9da8a76f06"}, +] + +[[package]] +name = "flake8-print" +version = "5.0.0" +description = "print statement checker plugin for flake8" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-print-5.0.0.tar.gz", hash = "sha256:76915a2a389cc1c0879636c219eb909c38501d3a43cc8dae542081c9ba48bdf9"}, + {file = "flake8_print-5.0.0-py3-none-any.whl", hash = "sha256:84a1a6ea10d7056b804221ac5e62b1cee1aefc897ce16f2e5c42d3046068f5d8"}, +] + +[package.dependencies] +flake8 = ">=3.0" +pycodestyle = "*" + +[[package]] +name = "flake8-pytest-style" +version = "1.6.0" +description = "A flake8 plugin checking common style issues or inconsistencies with pytest-based tests." +category = "dev" +optional = false +python-versions = ">=3.6.2,<4.0.0" +files = [ + {file = "flake8-pytest-style-1.6.0.tar.gz", hash = "sha256:c1175713e9e11b78cd1a035ed0bca0d1e41d09c4af329a952750b61d4194ddac"}, + {file = "flake8_pytest_style-1.6.0-py3-none-any.whl", hash = "sha256:5fedb371a950e9fe0e0e6bfc854be7d99151271208f34cd2cc517681ece27780"}, +] + +[package.dependencies] +flake8-plugin-utils = ">=1.3.2,<2.0.0" + +[[package]] +name = "flake8-string-format" +version = "0.3.0" +description = "string format checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "flake8-string-format-0.3.0.tar.gz", hash = "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2"}, + {file = "flake8_string_format-0.3.0-py2.py3-none-any.whl", hash = "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af"}, +] + +[package.dependencies] +flake8 = "*" + +[[package]] +name = "flake8-tidy-imports" +version = "4.8.0" +description = "A flake8 plugin that helps you write tidier imports." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-tidy-imports-4.8.0.tar.gz", hash = "sha256:df44f9c841b5dfb3a7a1f0da8546b319d772c2a816a1afefcce43e167a593d83"}, + {file = "flake8_tidy_imports-4.8.0-py3-none-any.whl", hash = "sha256:25bd9799358edefa0e010ce2c587b093c3aba942e96aeaa99b6d0500ae1bf09c"}, +] + +[package.dependencies] +flake8 = ">=3.8.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "furo" +version = "2022.12.7" +description = "A clean customisable Sphinx documentation theme." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "furo-2022.12.7-py3-none-any.whl", hash = "sha256:7cb76c12a25ef65db85ab0743df907573d03027a33631f17d267e598ebb191f7"}, + {file = "furo-2022.12.7.tar.gz", hash = "sha256:d8008f8efbe7587a97ba533c8b2df1f9c21ee9b3e5cad0d27f61193d38b1a986"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +pygments = ">=2.7" +sphinx = ">=5.0,<7.0" +sphinx-basic-ng = "*" + +[[package]] +name = "gitdb" +version = "4.0.10" +description = "Git Object Database" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gitdb-4.0.10-py3-none-any.whl", hash = "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"}, + {file = "gitdb-4.0.10.tar.gz", hash = "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a"}, +] + +[package.dependencies] +smmap = ">=3.0.1,<6" + +[[package]] +name = "gitpython" +version = "3.1.30" +description = "GitPython is a python library used to interact with Git repositories" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "GitPython-3.1.30-py3-none-any.whl", hash = "sha256:cd455b0000615c60e286208ba540271af9fe531fa6a87cc590a7298785ab2882"}, + {file = "GitPython-3.1.30.tar.gz", hash = "sha256:769c2d83e13f5d938b7688479da374c4e3d49f71549aaf462b646db9602ea6f8"}, +] + +[package.dependencies] +gitdb = ">=4.0.1,<5" +typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""} + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "importlib-metadata" +version = "6.0.0" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"}, + {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"}, +] + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.2" +description = "Safely add untrusted strings to HTML/XML markup." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mypy" +version = "0.991" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mypy-0.991-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab"}, + {file = "mypy-0.991-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d"}, + {file = "mypy-0.991-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6"}, + {file = "mypy-0.991-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb"}, + {file = "mypy-0.991-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305"}, + {file = "mypy-0.991-cp310-cp310-win_amd64.whl", hash = "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c"}, + {file = "mypy-0.991-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372"}, + {file = "mypy-0.991-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f"}, + {file = "mypy-0.991-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33"}, + {file = "mypy-0.991-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05"}, + {file = "mypy-0.991-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad"}, + {file = "mypy-0.991-cp311-cp311-win_amd64.whl", hash = "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297"}, + {file = "mypy-0.991-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813"}, + {file = "mypy-0.991-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711"}, + {file = "mypy-0.991-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd"}, + {file = "mypy-0.991-cp37-cp37m-win_amd64.whl", hash = "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef"}, + {file = "mypy-0.991-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a"}, + {file = "mypy-0.991-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93"}, + {file = "mypy-0.991-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf"}, + {file = "mypy-0.991-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135"}, + {file = "mypy-0.991-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70"}, + {file = "mypy-0.991-cp38-cp38-win_amd64.whl", hash = "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243"}, + {file = "mypy-0.991-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d"}, + {file = "mypy-0.991-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5"}, + {file = "mypy-0.991-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3"}, + {file = "mypy-0.991-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648"}, + {file = "mypy-0.991-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476"}, + {file = "mypy-0.991-cp39-cp39-win_amd64.whl", hash = "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461"}, + {file = "mypy-0.991-py3-none-any.whl", hash = "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb"}, + {file = "mypy-0.991.tar.gz", hash = "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06"}, +] + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] + +[[package]] +name = "packaging" +version = "23.0" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, + {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, +] + +[[package]] +name = "pathspec" +version = "0.11.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"}, + {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, +] + +[[package]] +name = "pbr" +version = "5.11.1" +description = "Python Build Reasonableness" +category = "dev" +optional = false +python-versions = ">=2.6" +files = [ + {file = "pbr-5.11.1-py2.py3-none-any.whl", hash = "sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b"}, + {file = "pbr-5.11.1.tar.gz", hash = "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"}, +] + +[[package]] +name = "pep8-naming" +version = "0.13.3" +description = "Check PEP-8 naming conventions, plugin for flake8" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, + {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[[package]] +name = "platformdirs" +version = "2.6.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, + {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} + +[package.extras] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "psutil" +version = "5.9.4" +description = "Cross-platform lib for process and system monitoring in Python." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "psutil-5.9.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c1ca331af862803a42677c120aff8a814a804e09832f166f226bfd22b56feee8"}, + {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:68908971daf802203f3d37e78d3f8831b6d1014864d7a85937941bb35f09aefe"}, + {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549"}, + {file = "psutil-5.9.4-cp27-cp27m-win32.whl", hash = "sha256:852dd5d9f8a47169fe62fd4a971aa07859476c2ba22c2254d4a1baa4e10b95ad"}, + {file = "psutil-5.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9120cd39dca5c5e1c54b59a41d205023d436799b1c8c4d3ff71af18535728e94"}, + {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24"}, + {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:efeae04f9516907be44904cc7ce08defb6b665128992a56957abc9b61dca94b7"}, + {file = "psutil-5.9.4-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7"}, + {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16653106f3b59386ffe10e0bad3bb6299e169d5327d3f187614b1cb8f24cf2e1"}, + {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54c0d3d8e0078b7666984e11b12b88af2db11d11249a8ac8920dd5ef68a66e08"}, + {file = "psutil-5.9.4-cp36-abi3-win32.whl", hash = "sha256:149555f59a69b33f056ba1c4eb22bb7bf24332ce631c44a319cec09f876aaeff"}, + {file = "psutil-5.9.4-cp36-abi3-win_amd64.whl", hash = "sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4"}, + {file = "psutil-5.9.4-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6001c809253a29599bc0dfd5179d9f8a5779f9dffea1da0f13c53ee568115e1e"}, + {file = "psutil-5.9.4.tar.gz", hash = "sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "py-cpuinfo" +version = "9.0.0" +description = "Get CPU info with pure Python" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"}, + {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, +] + +[[package]] +name = "pycodestyle" +version = "2.10.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, + {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, +] + +[[package]] +name = "pyflakes" +version = "3.0.1" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyflakes-3.0.1-py2.py3-none-any.whl", hash = "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf"}, + {file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"}, +] + +[[package]] +name = "pygments" +version = "2.14.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"}, + {file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pyproject-api" +version = "1.5.0" +description = "API to interact with the python pyproject.toml based projects" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyproject_api-1.5.0-py3-none-any.whl", hash = "sha256:4c111277dfb96bcd562c6245428f27250b794bfe3e210b8714c4f893952f2c17"}, + {file = "pyproject_api-1.5.0.tar.gz", hash = "sha256:0962df21f3e633b8ddb9567c011e6c1b3dcdfc31b7860c0ede7e24c5a1200fbe"}, +] + +[package.dependencies] +packaging = ">=21.3" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2022.9.29)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] +testing = ["covdefaults (>=2.2.2)", "importlib-metadata (>=5.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "virtualenv (>=20.17)", "wheel (>=0.38.4)"] + +[[package]] +name = "pyproject-hooks" +version = "1.0.0" +description = "Wrappers to call pyproject.toml-based build backend hooks." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyproject_hooks-1.0.0-py3-none-any.whl", hash = "sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8"}, + {file = "pyproject_hooks-1.0.0.tar.gz", hash = "sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5"}, +] + +[package.dependencies] +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "pytest" +version = "7.2.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.2.1-py3-none-any.whl", hash = "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5"}, + {file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"}, +] + +[package.dependencies] +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "pytest-benchmark" +version = "4.0.0" +description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-benchmark-4.0.0.tar.gz", hash = "sha256:fb0785b83efe599a6a956361c0691ae1dbb5318018561af10f3e915caa0048d1"}, + {file = "pytest_benchmark-4.0.0-py3-none-any.whl", hash = "sha256:fdb7db64e31c8b277dff9850d2a2556d8b60bcb0ea6524e36e28ffd7c87f71d6"}, +] + +[package.dependencies] +py-cpuinfo = "*" +pytest = ">=3.8" + +[package.extras] +aspect = ["aspectlib"] +elasticsearch = ["elasticsearch"] +histogram = ["pygal", "pygaljs"] + +[[package]] +name = "pytest-cov" +version = "4.0.0" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, + {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.10.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-mock-3.10.0.tar.gz", hash = "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f"}, + {file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "pytest-randomly" +version = "3.12.0" +description = "Pytest plugin to randomly order tests and control random.seed." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-randomly-3.12.0.tar.gz", hash = "sha256:d60c2db71ac319aee0fc6c4110a7597d611a8b94a5590918bfa8583f00caccb2"}, + {file = "pytest_randomly-3.12.0-py3-none-any.whl", hash = "sha256:f4f2e803daf5d1ba036cc22bf4fe9dbbf99389ec56b00e5cba732fb5c1d07fdd"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} +pytest = "*" + +[[package]] +name = "pytest-xdist" +version = "3.1.0" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-xdist-3.1.0.tar.gz", hash = "sha256:40fdb8f3544921c5dfcd486ac080ce22870e71d82ced6d2e78fa97c2addd480c"}, + {file = "pytest_xdist-3.1.0-py3-none-any.whl", hash = "sha256:70a76f191d8a1d2d6be69fc440cdf85f3e4c03c08b520fd5dc5d338d6cf07d89"}, +] + +[package.dependencies] +execnet = ">=1.1" +pytest = ">=6.2.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "pytz" +version = "2022.7.1" +description = "World timezone definitions, modern and historical" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, + {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, +] + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] + +[[package]] +name = "recommonmark" +version = "0.7.1" +description = "A docutils-compatibility bridge to CommonMark, enabling you to write CommonMark inside of Docutils & Sphinx projects." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, + {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, +] + +[package.dependencies] +commonmark = ">=0.8.1" +docutils = ">=0.11" +sphinx = ">=1.3.1" + +[[package]] +name = "requests" +version = "2.28.2" +description = "Python HTTP for Humans." +category = "dev" +optional = false +python-versions = ">=3.7, <4" +files = [ + {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, + {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "setuptools" +version = "66.1.1" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-66.1.1-py3-none-any.whl", hash = "sha256:6f590d76b713d5de4e49fe4fbca24474469f53c83632d5d0fd056f7ff7e8112b"}, + {file = "setuptools-66.1.1.tar.gz", hash = "sha256:ac4008d396bc9cd983ea483cb7139c0240a07bbc74ffb6232fceffedc6cf03a8"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "smmap" +version = "5.0.0" +description = "A pure Python implementation of a sliding window memory map manager" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, + {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "soupsieve" +version = "2.3.2.post1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, + {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, +] + +[[package]] +name = "sphinx" +version = "6.1.3" +description = "Python documentation generator" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "Sphinx-6.1.3.tar.gz", hash = "sha256:0dac3b698538ffef41716cf97ba26c1c7788dba73ce6f150c1ff5b4720786dd2"}, + {file = "sphinx-6.1.3-py3-none-any.whl", hash = "sha256:807d1cb3d6be87eb78a381c3e70ebd8d346b9a25f3753e9947e866b2786865fc"}, +] + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.18,<0.20" +imagesize = ">=1.3" +importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.13" +requests = ">=2.25.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] +test = ["cython", "html5lib", "pytest (>=4.6)"] + +[[package]] +name = "sphinx-autodoc-typehints" +version = "1.21.8" +description = "Type hints (PEP 484) support for the Sphinx autodoc extension" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx_autodoc_typehints-1.21.8-py3-none-any.whl", hash = "sha256:b8d15c05ced5e9fbe46a6dfd2ff194b6e508a64c9229bc7eaa08ee7089b9e6d3"}, + {file = "sphinx_autodoc_typehints-1.21.8.tar.gz", hash = "sha256:68e7136403ca67359d7beaab4d358cea518623abe749cc4e0639609aebf2f5c7"}, +] + +[package.dependencies] +sphinx = ">=5.3" + +[package.extras] +docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.21)"] +testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.5)", "diff-cover (>=7.3)", "nptyping (>=2.4.1)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "sphobjinv (>=2.3.1)", "typing-extensions (>=4.4)"] +type-comment = ["typed-ast (>=1.5.4)"] + +[[package]] +name = "sphinx-basic-ng" +version = "1.0.0b1" +description = "A modern skeleton for Sphinx themes." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx_basic_ng-1.0.0b1-py3-none-any.whl", hash = "sha256:ade597a3029c7865b24ad0eda88318766bcc2f9f4cef60df7e28126fde94db2a"}, + {file = "sphinx_basic_ng-1.0.0b1.tar.gz", hash = "sha256:89374bd3ccd9452a301786781e28c8718e99960f2d4f411845ea75fc7bb5a9b0"}, +] + +[package.dependencies] +sphinx = ">=4.0" + +[package.extras] +docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] + +[[package]] +name = "sphinx-copybutton" +version = "0.5.1" +description = "Add a copy button to each of your code cells." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx-copybutton-0.5.1.tar.gz", hash = "sha256:366251e28a6f6041514bfb5439425210418d6c750e98d3a695b73e56866a677a"}, + {file = "sphinx_copybutton-0.5.1-py3-none-any.whl", hash = "sha256:0842851b5955087a7ec7fc870b622cb168618ad408dee42692e9a5c97d071da8"}, +] + +[package.dependencies] +sphinx = ">=1.8" + +[package.extras] +code-style = ["pre-commit (==2.12.1)"] +rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] + +[[package]] +name = "sphinx-inline-tabs" +version = "2022.1.2b11" +description = "Add inline tabbed content to your Sphinx documentation." +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinx_inline_tabs-2022.1.2b11-py3-none-any.whl", hash = "sha256:bb4e807769ef52301a186d0678da719120b978a1af4fd62a1e9453684e962dbc"}, + {file = "sphinx_inline_tabs-2022.1.2b11.tar.gz", hash = "sha256:afb9142772ec05ccb07f05d8181b518188fc55631b26ee803c694e812b3fdd73"}, +] + +[package.dependencies] +sphinx = ">=3" + +[package.extras] +doc = ["furo", "myst-parser"] +test = ["pytest", "pytest-cov", "pytest-xdist"] + +[[package]] +name = "sphinx-tabs" +version = "3.4.1" +description = "Tabbed views for Sphinx" +category = "dev" +optional = false +python-versions = "~=3.7" +files = [ + {file = "sphinx-tabs-3.4.1.tar.gz", hash = "sha256:d2a09f9e8316e400d57503f6df1c78005fdde220e5af589cc79d493159e1b832"}, + {file = "sphinx_tabs-3.4.1-py3-none-any.whl", hash = "sha256:7cea8942aeccc5d01a995789c01804b787334b55927f29b36ba16ed1e7cb27c6"}, +] + +[package.dependencies] +docutils = ">=0.18.0,<0.19.0" +pygments = "*" +sphinx = "*" + +[package.extras] +code-style = ["pre-commit (==2.13.0)"] +testing = ["bs4", "coverage", "pygments", "pytest (>=7.1,<8)", "pytest-cov", "pytest-regressions", "rinohtype", "sphinx-testing"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.4" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, + {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.0" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, + {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "stevedore" +version = "3.5.2" +description = "Manage dynamic plugins for Python applications" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "stevedore-3.5.2-py3-none-any.whl", hash = "sha256:fa2630e3d0ad3e22d4914aff2501445815b9a4467a6edc49387c667a38faf5bf"}, + {file = "stevedore-3.5.2.tar.gz", hash = "sha256:cf99f41fc0d5a4f185ca4d3d42b03be9011b0a1ec1a4ea1a282be1b4b306dcc2"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""} +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tox" +version = "4.4.0" +description = "tox is a generic virtualenv management and test command line tool" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tox-4.4.0-py3-none-any.whl", hash = "sha256:a9aaef6ee45714bcc28c99aa91b6a8c098eca6b67d361c6d3ea033a82acfca22"}, + {file = "tox-4.4.0.tar.gz", hash = "sha256:597d9e55e2b476e5b92cfedea7d4132b0ab90e6a23ba93774f47763239545d75"}, +] + +[package.dependencies] +cachetools = ">=5.2.1" +chardet = ">=5.1" +colorama = ">=0.4.6" +filelock = ">=3.9" +importlib-metadata = {version = ">=6", markers = "python_version < \"3.8\""} +packaging = ">=23" +platformdirs = ">=2.6.2" +pluggy = ">=1" +pyproject-api = ">=1.5" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} +virtualenv = ">=20.17.1" + +[package.extras] +docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-argparse-cli (>=1.11)", "sphinx-autodoc-typehints (>=1.20.1)", "sphinx-copybutton (>=0.5.1)", "sphinx-inline-tabs (>=2022.1.2b11)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +testing = ["build[virtualenv] (>=0.9)", "covdefaults (>=2.2.2)", "devpi-process (>=0.3)", "diff-cover (>=7.3)", "distlib (>=0.3.6)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.12.2)", "psutil (>=5.9.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-xdist (>=3.1)", "re-assert (>=1.1)", "time-machine (>=2.8.2)", "wheel (>=0.38.4)"] + +[[package]] +name = "tox-gh-actions" +version = "3.0.0" +description = "Seamless integration of tox into GitHub Actions" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tox-gh-actions-3.0.0.tar.gz", hash = "sha256:1a20286a1102c720a5979f56a060f6d618306dbb660a6724a31d89f397421e5c"}, + {file = "tox_gh_actions-3.0.0-py2.py3-none-any.whl", hash = "sha256:059ebfdba76864819f3a4629c3404be094dae9dc8e7922c7c7a9999b9551f82a"}, +] + +[package.dependencies] +tox = ">=4,<5" + +[package.extras] +testing = ["black", "devpi-process", "flake8 (>=6,<7)", "mypy", "pytest (>=7,<8)", "pytest-cov (>=3,<4)", "pytest-mock (>=3,<4)", "pytest-randomly (>=3)"] + +[[package]] +name = "typed-ast" +version = "1.5.4" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, +] + +[[package]] +name = "typing-extensions" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, + {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, +] + +[[package]] +name = "urllib3" +version = "1.26.14" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"}, + {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "validate-pyproject" +version = "0.12" +description = "Validation library and CLI tool for checking on 'pyproject.toml' files using JSON Schema" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "validate-pyproject-0.12.tar.gz", hash = "sha256:1a87b19b0db585e70c2053f8e8eaa586f1fae342b2387c8dc92b4ce776a64508"}, + {file = "validate_pyproject-0.12-py3-none-any.whl", hash = "sha256:517fb56c32f7d3c139678dd897d7bed7f83c157ef637e5969ad88c4ebc38dc89"}, +] + +[package.dependencies] +fastjsonschema = ">=2.16.2,<=3" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +all = ["packaging (>=20.4)", "tomli (>=1.2.1)", "trove-classifiers (>=2021.10.20)"] +testing = ["pytest", "pytest-cov", "pytest-randomly", "pytest-xdist", "setuptools"] +typecheck = ["importlib-resources", "mypy"] + +[[package]] +name = "virtualenv" +version = "20.17.1" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "virtualenv-20.17.1-py3-none-any.whl", hash = "sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4"}, + {file = "virtualenv-20.17.1.tar.gz", hash = "sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"}, +] + +[package.dependencies] +distlib = ">=0.3.6,<1" +filelock = ">=3.4.1,<4" +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""} +platformdirs = ">=2.4,<3" + +[package.extras] +docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "vulture" +version = "2.7" +description = "Find dead code" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "vulture-2.7-py2.py3-none-any.whl", hash = "sha256:bccc51064ed76db15a6b58277cea8885936af047f53d2655fb5de575e93d0bca"}, + {file = "vulture-2.7.tar.gz", hash = "sha256:67fb80a014ed9fdb599dd44bb96cb54311032a104106fc2e706ef7a6dad88032"}, +] + +[package.dependencies] +toml = "*" + +[[package]] +name = "zipp" +version = "3.11.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, + {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.7" +content-hash = "24fe265ef25bfdea3aef075cc90243b962de6c7c40011157f247e703baa955a8" diff --git a/pyproject.toml b/pyproject.toml index c75b2da..5c7cb9d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,11 @@ [build-system] -requires = ["setuptools>=62.0"] -build-backend = "setuptools.build_meta" +requires = ["poetry-core>=1.2.0"] +build-backend = "poetry.core.masonry.api" [project] name = "getmac" dynamic = ["version", "readme"] +# TODO: add contributors to authors? authors = [ { name="Christopher Goes", email="ghostofgoes@gmail.com" } ] @@ -73,25 +74,159 @@ keywords=[ [project.scripts] getmac = "getmac.__main__:main" -# TODO: move from requirement files to optional deps managed by poetry -# [project.optional-dependencies] -# tests = [ -# "coverage[toml]", -# "pytest>=6.0", -# ... -# ] -# docs = [ -# "furo", -# "myst-parser", -# "sphinx", -# "sphinx-notfound-page", -# "sphinxcontrib-mermaid", -# ... -# ] -# dev = [ -# "pre-commit", -# ... -# ] + +[tool.poetry] +name = "getmac" +version = "1.0.0a0" +description = "" +license = "MIT" +# TODO: add contributors to authors? +authors = [ + "Christopher Goes " +] +maintainers = [ + "Christopher Goes " +] +# TODO (before 1.0.0 is released): change to ["README.md", "CHANGELOG.md"] once poetry updates +# https://github.com/python-poetry/poetry/issues/6633 +readme = "README.md" +repository = "https://github.com/GhostofGoes/getmac" +# documentation = "" # TODO +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX", + "Operating System :: POSIX :: BSD", + "Operating System :: POSIX :: BSD :: FreeBSD", + "Operating System :: POSIX :: BSD :: NetBSD", + "Operating System :: POSIX :: BSD :: OpenBSD", + "Operating System :: POSIX :: Linux", + "Operating System :: POSIX :: Other", + "Operating System :: MacOS", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + "Intended Audience :: Information Technology", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: Libraries", + "Topic :: System :: Systems Administration", + "Topic :: System :: Networking", + "Topic :: Utilities", + "Typing :: Typed" +] +keywords=[ + "getmac", + "get-mac", + "macaddress", + "mac-address", + "mac", + "ethernet", + "mac-48", + "networking", + "network", + "networking", + "layer2", + "layer-2", + "802.3" +] +packages = [ + { include = "getmac" } +] +include = [ + { path = "tests", format = "sdist" }, + { path = "CHANGELOG.md", format = "sdist" }, + { path = "CONTRIBUTING.md", format = "sdist"}, + { path = "SECURITY.md", format = "sdist" }, + { path = "pyproject.toml", format = "sdist" }, + { path = "tox.ini", format = "sdist" }, + { path = "getmac" }, + { path = "LICENSE" }, + { path = "README.md" }, +] + +[tool.poetry.scripts] +getmac = 'getmac.__main__:main' + +[tool.poetry.urls] +"Homepage" = "https://github.com/GhostofGoes/getmac" +# TODO: get ReadTheDocs working +# "Documentation" = "https://getmac.readthedocs.io/en/latest/" +"Changelog" = "https://github.com/GhostofGoes/getmac/blob/main/CHANGELOG.md" +"Issue tracker" = "https://github.com/GhostofGoes/getmac/issues" +"Source" = "https://github.com/GhostofGoes/getmac" +"Discord server" = "https://discord.gg/python" + +[tool.poetry.dependencies] +python = "^3.7" + +[tool.poetry.group.dev.dependencies] +tox = "^4" + +[tool.poetry.group.ci.dependencies] +tox = "^4" +tox-gh-actions = "*" +coveralls = "*" +tomli = { version = "*", python = "<3.12" } + +[tool.poetry.group.test.dependencies] +pytest = "^7" +pytest-randomly = "*" +pytest-mock = "*" +pytest-benchmark = "*" +pytest-cov = "*" +pytest-xdist = "*" +psutil = "*" + +[tool.poetry.group.lint.dependencies] +check-manifest = "^0.49" +codespell = ">=2.2.2" +tomli = { version = "*", python = "<3.12" } +flake8 = { version = "*", python = ">=3.8.1,<4.0" } +flake8-bugbear = "^23.1.20" +flake8-import-order = "^0.18.2" +flake8-tidy-imports = "^4.8.0" +flake8-print = "^5.0.0" +flake8-builtins = "^2.1.0" +flake8-blind-except = "^0.2.1" +flake8-comprehensions = "^3.10.1" +flake8-debugger = "^4.1.2" +pep8-naming = "^0.13.3" +flake8-string-format = "^0.3.0" +flake8-mutable = "^1.2.0" +flake8-pytest-style = "^1.6.0" +# TODO: flake8-annotations 3.0.0 removes support for PEP-484 type comments +flake8-annotations = "<3.0.0" +flake8-black = "*" +# TODO: replace flake8-mypy with proper execution of mypy +# NOTE: the released version of flake8-mypy is buggy on Windows (17.8.0) +flake8-mypy = { git = "https://github.com/ambv/flake8-mypy.git" } +bandit = "^1.6.0" +black = "^22.12.0" +vulture = "^2.7" +validate-pyproject = "*" +mypy = "*" + + +[tool.poetry.group.docs.dependencies] +furo = {version = "*", python = ">=3.8,<4.0"} +sphinx = {version = "^6.1.3", python = ">=3.8,<4.0"} +recommonmark = {version = "^0.7.1", python = ">=3.8,<4.0"} +sphinx-autodoc-typehints = {version = "^1.21.8", python = ">=3.8,<4.0"} +sphinx-copybutton = {version = "^0.5.1", python = ">=3.8,<4.0"} +sphinx-inline-tabs = {version = "^2022.1.2b11", python = ">=3.8,<4.0"} +sphinx-tabs = {version = "^3.4.1", python = ">=3.8,<4.0"} [tool.setuptools] @@ -109,9 +244,6 @@ readme = { file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown" testpaths = "tests" norecursedirs = ".github getmac samples scripts .git .tox .vagrant .idea .vscode dist build *.egg .* .venv" addopts = "--tb=short" -# filterwarnings = [ -# "ignore::DeprecationWarning" -# ] [tool.coverage.run] relative_files = true diff --git a/tests/style-requirements.txt b/tests/style-requirements.txt deleted file mode 100644 index c1d88e4..0000000 --- a/tests/style-requirements.txt +++ /dev/null @@ -1,27 +0,0 @@ -check-manifest==0.49 -codespell==2.2.2 -tomli -flake8>=5.0.0 -flake8-bugbear==23.1.20 -flake8-import-order==0.18.2 -flake8-tidy-imports==4.8.0 -flake8-print==5.0.0 -flake8-builtins==2.1.0 -flake8-blind-except==0.2.1 -flake8-comprehensions==3.10.1 -flake8-debugger==4.1.2 -pep8-naming==0.13.3 -flake8-string-format==0.3.0 -flake8-broken-line==0.6.0 -flake8-mutable==1.2.0 -flake8-pytest-style==1.6.0 -# TODO: flake8-annotations 3.0.0 removes support for PEP-484 type comments -flake8-annotations<3.0.0 -flake8-black -# TODO: replace flake8-mypy with proper execution of mypy -# NOTE: the released version of flake8-mypy is buggy on Windows (17.8.0) -https://github.com/ambv/flake8-mypy/archive/master.tar.gz -bandit>=1.6.0 -black==22.12.0 -vulture==2.7 -validate-pyproject==0.11 diff --git a/tests/test-requirements.txt b/tests/test-requirements.txt deleted file mode 100644 index d497c96..0000000 --- a/tests/test-requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -pytest -pytest-randomly -pytest-mock -pytest-benchmark -pytest-cov -pytest-xdist -psutil diff --git a/tox.ini b/tox.ini index 186a4c7..0b0a750 100644 --- a/tox.ini +++ b/tox.ini @@ -10,11 +10,11 @@ python = 3.10: py310 3.11: py311 3.12: py312 - pypy-3.7: pypy3 - pypy-3.8: pypy3 - pypy-3.9: pypy3 - pypy-3.10: pypy3 - pypy-3.11: pypy3 + pypy3.7: pypy3 + pypy3.8: pypy3 + pypy3.9: pypy3 + pypy3.10: pypy3 + pypy3.11: pypy3 [gh-actions:env] PLATFORM = @@ -35,32 +35,33 @@ platform = linux: linux macos: darwin windows: win32 -deps = -rtests/test-requirements.txt +allowlist_externals = + poetry ; TODO: --benchmark-enable? commands = - pytest --basetemp={envtmpdir} --benchmark-disable --cov=getmac \ - --cov-report term --cov-report html --cov-report xml \ - -n auto {posargs} + poetry run pytest \ + --basetemp={envtmpdir} --benchmark-disable --cov=getmac \ + --cov-report term --cov-report html --cov-report xml \ + -n auto {posargs} [testenv:benchmark] description = Run the test suite with benchmarks enabled commands = - pytest --basetemp={envtmpdir} --benchmark-enable -n auto {posargs} + poetry run pytest --basetemp={envtmpdir} --benchmark-enable -n auto {posargs} [testenv:check] description = Run code quality, static analysis, and spelling checks setenv = PIP_DISABLE_PIP_VERSION_CHECK = 1 MYPYPATH=getmac -deps = -rtests/style-requirements.txt basepython = py311, py310, py3 commands = - codespell - flake8 getmac tests - bandit -q -r -c {toxinidir}/pyproject.toml getmac - vulture --min-confidence 100 getmac tests - validate-pyproject {toxinidir}/pyproject.toml - check-manifest --quiet {toxinidir} + poetry run codespell + poetry run flake8 getmac tests + poetry run bandit -q -r -c {toxinidir}/pyproject.toml getmac + poetry run vulture --min-confidence 100 getmac tests + poetry run validate-pyproject {toxinidir}/pyproject.toml + poetry run check-manifest --quiet {toxinidir} [flake8] max-line-length = 99 From 54d138c8ea5f658576d88a3810f3bcb0437708f5 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 25 Jan 2023 15:06:10 -0600 Subject: [PATCH 012/112] fix poetry lint --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5e9283..6e71dfc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 - name: Install dependencies (poetry) - run: poetry install --only=main,lint --no-interaction + run: poetry install --only=main,lint,ci --no-interaction - name: Run lint checks run: poetry run tox -e check test: From 974fcd80cef4d49b3e332b1e7fe972ac9de496bb Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 1 Jun 2023 21:14:49 -0600 Subject: [PATCH 013/112] README updates and improvements --- README.md | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index c9c76dc..1a17307 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ getmac --ip 192.168.0.1 --override-platform windows getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost ``` -## Function: get_mac_address() +## `get_mac_address()` - `interface`: Name of a network interface on the system - `ip`: IPv4 address of a remote host - `ip6`: IPv6 address of a remote host @@ -136,19 +136,6 @@ getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost - Provides a simple command line tool (when installed as a package) - MIT licensed! -## Legacy Python versions -If you are running a old Python (2.6/3.3 and older) or interpreter, then you can install an older version of `getmac` that supported that version. The wheels are available in the [GitHub releases](https://github.com/GhostofGoes/getmac/releases), or from PyPI with a current version of `pip` and some special arguments. - -- Python 2.5: get-mac 0.5.0 -- Python 2.6: getmac 0.6.0 -- Python 2.7: getmac 0.9.0 -- Python 3.2: get-mac 0.3.0 -- Python 3.3: get-mac 0.3.0 -- Python 3.4: getmac 0.9.0 -- Python 3.5: getmac 0.9.0 - -NOTE: these versions do not have many of the performance improvements, platform support, and bug fixes that came with later releases. They generally work, just not as well. However, if you're using such an old Python, you probably don't care about all that :) - ## Notes - If none of the arguments are selected, the default network interface for the system will be used. If the default network interface cannot be determined, then it will attempt to fallback to typical defaults for the platform (`Ethernet` on Windows, `em0` on BSD, `en0` on OSX/Darwin, and `eth0` otherwise). If that fails, then it will fallback to `lo` on POSIX systems. - "Remote hosts" refer to hosts in your local layer 2 network, also commonly referred to as a "broadcast domain", "LAN", or "VLAN". As far as I know, there is not a reliable method to get a MAC address for a remote host external to the LAN. If you know any methods otherwise, please [open a GitHub issue](https://github.com/GhostofGoes/getmac/issues) or shoot me an email, I'd love to be wrong about this. @@ -161,13 +148,13 @@ NOTE: these versions do not have many of the performance improvements, platform - Commands: `getmac.exe`, `ipconfig.exe`, `arp.exe`, `wmic.exe` - Libraries: `uuid`, `ctypes`, `socket` - Linux/Unix - - Commands: `arp`, `ip`, `ifconfig`, `netstat`, `ip link`, `lanscan` + - Commands: `arp`, `ip`, `ifconfig`, `netstat`, `ip link`, `lanscan`, `arping` (both iputils and Habet's variants) - Libraries: `uuid`, `fcntl`, `socket` - Files: `/sys/class/net/{iface}/address`, `/proc/net/arp` - Default interfaces: `/proc/net/route`, `route`, `ip route list` - Mac OSX (Darwin) - - `networksetup` - - Same commands as Linux + - `networksetup`, `arping` + - Many of the same commands as Linux - WSL - Windows commands are used for remote hosts - Unix commands are used for interfaces @@ -198,6 +185,21 @@ All or almost all features should work on "supported" platforms. While other ver - FreeBSD (11+) - OpenBSD - Docker +- BusyBox (not technically a platform, but several of the methods work on this platform) + +## Legacy Python versions +If you are running a old Python (2.6/3.3 and older) or interpreter, then you can install an older version of `getmac` that supported that version. The wheels are available in the [GitHub releases](https://github.com/GhostofGoes/getmac/releases), or from PyPI with a current version of `pip` and some special arguments. + +- Python 2.5: `get-mac==0.5.0` +- Python 2.6: `getmac==0.6.0` +- Python 2.7: `getmac<1.0.0` +- Python 3.2: `get-mac==0.3.0` +- Python 3.3: `get-mac==0.3.0` +- Python 3.4: `getmac<1.0.0` +- Python 3.5: `getmac<1.0.0` +- Python 3.6: `getmac<1.0.0` + +NOTE: these versions do not have many of the performance improvements, platform support, and bug fixes that came with later releases. They generally work, just not as well. However, if you're using such an old Python, you probably don't care about all that :) ## Docker Add `-v /proc/1/net/arp:/host/arp -e ARP_PATH=/host/arp` to access arp table of host inside container in bridge network mode. From 94e1769bb25ad3d32987a6156a117a7bae5fd6d1 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 1 Jun 2023 21:28:06 -0600 Subject: [PATCH 014/112] Update contributing guide --- CONTRIBUTING.md | 69 ++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 04fb6e0..aad7f82 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,6 @@ # Contributing to getmac -Thanks for taking an interest in this awesome little project. We love -to bring new members into the community, and can always use the help. +Thanks for taking an interest in this awesome little project. We love to bring new members into the community, and can always use the help. ## Resources * Task tracking and Bug reports: [GitHub](https://github.com/GhostofGoes/getmac/issues) @@ -12,14 +11,13 @@ to bring new members into the community, and can always use the help. * Must work under all supported Python versions (2.7 and 3.4+) * Must work on all supported platforms (if applicable) * Try to match the general code style (loosely PEP8) -* Be respectful. Memes, references, and jokes are ok. Explicit language -(cursing/swearing), NSFW text/content, or racism are NOT ok. +* Be respectful. Memes, references, and jokes are ok. Explicit language (cursing/swearing), NSFW text/content, or racism are NOT ok. ## Checklist before submitting a pull request * [ ] Code is formatted using `black` (`black getmac tests`) * [ ] All tests run and pass locally - * [ ] `tox` - * [ ] `tox -e check` + * [ ] `poetry run tox` + * [ ] `poetry run tox -e check` * [ ] Update the [CHANGELOG](CHANGELOG.md) (For non-trivial changes, e.g. changing functionality or adding tests) * [ ] Add your name to the contributors list in the [README](README.md) @@ -44,9 +42,7 @@ to bring new members into the community, and can always use the help. * Adding tests for internal methods and mocking where necessary ## Platform testing -Help is dearly needed on testing and rooting out differences in various platforms and configurations. -At a basic level, this involves just running the tests on any platforms you use. -Open issues for any bugs or quirks you discover, or if you're feeling adventurous, fix it yourself! +Help is dearly needed on testing and rooting out differences in various platforms and configurations. At a basic level, this involves just running the tests on any platforms you use. Open issues for any bugs or quirks you discover, or if you're feeling adventurous, fix it yourself! Any platform is fair game! The following are some highlights or platforms of special interest: * MacOS/OSX (This requires owning a Mac, and is the area most sorely in need of testing) @@ -64,29 +60,24 @@ Examples of output of various commands is an easy way contribute that is still i # Getting started 1. Create your own fork of the code through GitHub web interface ([Here's a Guide](https://gist.github.com/Chaser324/ce0505fbed06b947d962)) -2. Clone the fork to your computer. This can be done using the -[GitHub desktop](https://desktop.github.com/) GUI , `git clone `, -or the Git tools in your favorite editor or IDE. -3. Create and checkout a new branch in the fork with either your username (e.g. "ghostofgoes"), -or the name of the feature or issue you're working on (e.g. "openbsd-support"). -Again, this can be done using the GUI, your favorite editor, or `git checkout -b origin/`. -4. Install the developer tools and getmac as an "editable" package: +1. Clone the fork to your computer. This can be done using the [GitHub desktop](https://desktop.github.com/) GUI , `git clone `, or the Git tools in your favorite editor or IDE. +1. Create and checkout a new branch in the fork with either your username (e.g. "ghostofgoes"), or the name of the feature or issue you're working on (e.g. "openbsd-support"). Again, this can be done using the GUI, your favorite editor, or `git checkout -b origin/`. +1. Install Poetry: https://python-poetry.org/docs/ +1. Create local environment: ```bash - python -m pip install --user -U tox - python -m pip install -e . + poetry install ``` -5. Setup and run the tests: +1. Ensure tests work locally: ```bash # Run code quality checks - tox -e check + poetry run tox -e check # Run the tests - tox + poetry run tox ``` -6. Write some code! Git commit messages should information about what changed, -and if it's relevant, the rationale (thinking) for the change. -7. Follow the checklist in the pull request template -8. Submit a pull request! +1. Write some code! Git commit messages should information about what changed, and if it's relevant, the rationale (thinking) for the change. +1. Follow the checklist in the pull request template +1. Submit a pull request! # Bug reports @@ -102,13 +93,11 @@ Filing a bug report: 2. Put any excessive output into a [GitHub Gist](https://gist.github.com/) and include a link in the issue. 3. Tag the issue with "Bug" -**NOTE**: If the issue is a potential security vulnerability, do *NOT* open an issue! -Instead, email: ghostofgoes(at)gmail(dot)com +**NOTE**: If the issue is a potential security vulnerability, do *NOT* open an issue! Instead, email: ghostofgoes(at)gmail(dot)com # Features and ideas -Ideas for features or other things are welcomed. Open an issue on GitHub -detailing the idea, and tag it appropriately (e.g. "Feature" for a new feature). +Ideas for features or other things are welcomed. Open an issue on GitHub detailing the idea, and tag it appropriately (e.g. "Feature" for a new feature). # Resources @@ -119,3 +108,25 @@ detailing the idea, and tag it appropriately (e.g. "Feature" for a new feature). - [Python's `re` documentation](https://docs.python.org/3/library/re.html) - [Python's guide to regex](https://docs.python.org/3/howto/regex.html) (this is actually really helpful) - https://ultrapico.com/Expresso.htm (I haven't used this but it looks useful) + + +# Commands +```bash +# Create development environment +poetry install + +# Update poetry.lock +poetry update + +# Run tests +poetry run tox + +# Lint checks +poetry run tox -e check + +# Run specific python tests (in this example, python 3.9) +poetry run tox -e py39 + +# Run getmac CLI +poetry run getmac +``` From 8770dbb0b169ff5127a8f25e2336e634f2fbe056 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 1 Jun 2023 21:29:20 -0600 Subject: [PATCH 015/112] update poetry.lock Package operations: 3 installs, 38 updates, 0 removals --- poetry.lock | 779 ++++++++++++++++++++++++---------------------------- 1 file changed, 359 insertions(+), 420 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3cc770b..8fb3fdc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "alabaster" version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -14,71 +13,71 @@ files = [ [[package]] name = "attrs" -version = "22.2.0" +version = "23.1.0" description = "Classes Without Boilerplate" -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, - {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, ] +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + [package.extras] -cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] -tests = ["attrs[tests-no-zope]", "zope.interface"] -tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] [[package]] name = "babel" -version = "2.11.0" +version = "2.12.1" description = "Internationalization utilities" -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"}, - {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"}, + {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, + {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, ] [package.dependencies] -pytz = ">=2015.7" +pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} [[package]] name = "bandit" -version = "1.7.4" +version = "1.7.5" description = "Security oriented static analyser for python code." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"}, - {file = "bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"}, + {file = "bandit-1.7.5-py3-none-any.whl", hash = "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549"}, + {file = "bandit-1.7.5.tar.gz", hash = "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"}, ] [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} GitPython = ">=1.0.1" PyYAML = ">=5.3.1" +rich = "*" stevedore = ">=1.20.0" [package.extras] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "toml"] -toml = ["toml"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"] +toml = ["tomli (>=1.1.0)"] yaml = ["PyYAML"] [[package]] name = "beautifulsoup4" -version = "4.11.1" +version = "4.12.2" description = "Screen-scraping library" -category = "dev" optional = false python-versions = ">=3.6.0" files = [ - {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, - {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, + {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, + {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, ] [package.dependencies] @@ -92,7 +91,6 @@ lxml = ["lxml"] name = "black" version = "22.12.0" description = "The uncompromising code formatter." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -129,7 +127,6 @@ uvloop = ["uvloop (>=0.15.2)"] name = "build" version = "0.10.0" description = "A simple, correct Python build frontend" -category = "dev" optional = false python-versions = ">= 3.7" files = [ @@ -152,33 +149,30 @@ virtualenv = ["virtualenv (>=20.0.35)"] [[package]] name = "cachetools" -version = "5.3.0" +version = "5.3.1" description = "Extensible memoizing collections and decorators" -category = "dev" optional = false -python-versions = "~=3.7" +python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, - {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, + {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, + {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, ] [[package]] name = "certifi" -version = "2022.12.7" +version = "2023.5.7" description = "Python package for providing Mozilla's CA Bundle." -category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, ] [[package]] name = "chardet" version = "5.1.0" description = "Universal encoding detector for Python 3" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -188,107 +182,92 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.0.1" +version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "dev" optional = false -python-versions = "*" -files = [ - {file = "charset-normalizer-3.0.1.tar.gz", hash = "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-win32.whl", hash = "sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-win32.whl", hash = "sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-win32.whl", hash = "sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-win32.whl", hash = "sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-win32.whl", hash = "sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59"}, - {file = "charset_normalizer-3.0.1-py3-none-any.whl", hash = "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24"}, +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, ] [[package]] name = "check-manifest" version = "0.49" description = "Check MANIFEST.in in a Python source package for completeness" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -308,7 +287,6 @@ test = ["mock (>=3.0.0)", "pytest"] name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -322,26 +300,25 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "codespell" -version = "2.2.2" +version = "2.2.4" description = "Codespell" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "codespell-2.2.2-py3-none-any.whl", hash = "sha256:87dfcd9bdc9b3cb8b067b37f0af22044d7a84e28174adfc8eaa203056b7f9ecc"}, - {file = "codespell-2.2.2.tar.gz", hash = "sha256:c4d00c02b5a2a55661f00d5b4b3b5a710fa803ced9a9d7e45438268b099c319c"}, + {file = "codespell-2.2.4-py3-none-any.whl", hash = "sha256:7d984b8130108e6f82524b7d09f8b7bf2fb1e398c5d4b37d9e2bd310145b3e29"}, + {file = "codespell-2.2.4.tar.gz", hash = "sha256:0b4620473c257d9cde1ff8998b26b2bb209a35c2b7489f5dc3436024298ce83a"}, ] [package.extras] -dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency", "tomli"] +dev = ["Pygments", "build", "chardet", "flake8", "flake8-pyproject", "pytest", "pytest-cov", "pytest-dependency", "tomli"] hard-encoding-detection = ["chardet"] toml = ["tomli"] +types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -353,7 +330,6 @@ files = [ name = "commonmark" version = "0.9.1" description = "Python parser for the CommonMark Markdown spec" -category = "dev" optional = false python-versions = "*" files = [ @@ -368,7 +344,6 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] name = "coverage" version = "6.5.0" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -434,7 +409,6 @@ toml = ["tomli"] name = "coveralls" version = "3.3.1" description = "Show coverage stats online via coveralls.io" -category = "dev" optional = false python-versions = ">= 3.5" files = [ @@ -443,7 +417,7 @@ files = [ ] [package.dependencies] -coverage = ">=4.1,<6.0.0 || >6.1,<6.1.1 || >6.1.1,<7.0" +coverage = ">=4.1,<6.0.dev0 || >6.1,<6.1.1 || >6.1.1,<7.0" docopt = ">=0.6.1" requests = ">=1.0.0" @@ -454,7 +428,6 @@ yaml = ["PyYAML (>=3.10)"] name = "distlib" version = "0.3.6" description = "Distribution utilities" -category = "dev" optional = false python-versions = "*" files = [ @@ -466,7 +439,6 @@ files = [ name = "docopt" version = "0.6.2" description = "Pythonic argument parser, that will make you smile" -category = "dev" optional = false python-versions = "*" files = [ @@ -477,7 +449,6 @@ files = [ name = "docutils" version = "0.18.1" description = "Docutils -- Python Documentation Utilities" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -487,14 +458,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.1.0" +version = "1.1.1" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"}, - {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"}, + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, ] [package.extras] @@ -504,7 +474,6 @@ test = ["pytest (>=6)"] name = "execnet" version = "1.9.0" description = "execnet: rapid multi-Python deployment" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -517,14 +486,13 @@ testing = ["pre-commit"] [[package]] name = "fastjsonschema" -version = "2.16.2" +version = "2.17.1" description = "Fastest Python implementation of JSON schema" -category = "dev" optional = false python-versions = "*" files = [ - {file = "fastjsonschema-2.16.2-py3-none-any.whl", hash = "sha256:21f918e8d9a1a4ba9c22e09574ba72267a6762d47822db9add95f6454e51cc1c"}, - {file = "fastjsonschema-2.16.2.tar.gz", hash = "sha256:01e366f25d9047816fe3d288cbfc3e10541daf0af2044763f3d0ade42476da18"}, + {file = "fastjsonschema-2.17.1-py3-none-any.whl", hash = "sha256:4b90b252628ca695280924d863fe37234eebadc29c5360d322571233dc9746e0"}, + {file = "fastjsonschema-2.17.1.tar.gz", hash = "sha256:f4eeb8a77cef54861dbf7424ac8ce71306f12cbb086c45131bcba2c6a4f726e3"}, ] [package.extras] @@ -532,25 +500,23 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc [[package]] name = "filelock" -version = "3.9.0" +version = "3.12.0" description = "A platform independent file lock." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "filelock-3.9.0-py3-none-any.whl", hash = "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"}, - {file = "filelock-3.9.0.tar.gz", hash = "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de"}, + {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, + {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, ] [package.extras] -docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] -testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] [[package]] name = "flake8" version = "6.0.0" description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" optional = false python-versions = ">=3.8.1" files = [ @@ -567,7 +533,6 @@ pyflakes = ">=3.0.0,<3.1.0" name = "flake8-annotations" version = "2.9.1" description = "Flake8 Type Annotation Checks" -category = "dev" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -584,7 +549,6 @@ typed-ast = {version = ">=1.4,<2.0", markers = "python_version < \"3.8\""} name = "flake8-black" version = "0.3.6" description = "flake8 plugin to call black as a code style validator" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -604,7 +568,6 @@ develop = ["build", "twine"] name = "flake8-blind-except" version = "0.2.1" description = "A flake8 extension that checks for blind except: statements" -category = "dev" optional = false python-versions = "*" files = [ @@ -613,14 +576,13 @@ files = [ [[package]] name = "flake8-bugbear" -version = "23.1.20" +version = "23.3.12" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "flake8-bugbear-23.1.20.tar.gz", hash = "sha256:55902ab5a48c5ea53d8689ecd146eda548e72f2724192b9c1d68f6d975d13c06"}, - {file = "flake8_bugbear-23.1.20-py3-none-any.whl", hash = "sha256:04a115e5f9c8e87c38bdbbcdf9f58223ffe05469c07c9a7bd8633330bc4d078b"}, + {file = "flake8-bugbear-23.3.12.tar.gz", hash = "sha256:e3e7f74c8a49ad3794a7183353026dabd68c74030d5f46571f84c1fb0eb79363"}, + {file = "flake8_bugbear-23.3.12-py3-none-any.whl", hash = "sha256:beb5c7efcd7ccc2039ef66a77bb8db925e7be3531ff1cb4d0b7030d0e2113d72"}, ] [package.dependencies] @@ -634,7 +596,6 @@ dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", name = "flake8-builtins" version = "2.1.0" description = "Check for python builtins being used as variables or parameters." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -650,14 +611,13 @@ test = ["pytest"] [[package]] name = "flake8-comprehensions" -version = "3.10.1" +version = "3.12.0" description = "A flake8 plugin to help you write better list/set/dict comprehensions." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "flake8-comprehensions-3.10.1.tar.gz", hash = "sha256:412052ac4a947f36b891143430fef4859705af11b2572fbb689f90d372cf26ab"}, - {file = "flake8_comprehensions-3.10.1-py3-none-any.whl", hash = "sha256:d763de3c74bc18a79c039a7ec732e0a1985b0c79309ceb51e56401ad0a2cd44e"}, + {file = "flake8_comprehensions-3.12.0-py3-none-any.whl", hash = "sha256:013234637ec7dfcb7cd2900578fb53c512f81db909cefe371c019232695c362d"}, + {file = "flake8_comprehensions-3.12.0.tar.gz", hash = "sha256:419ef1a6e8de929203791a5e8ff5e3906caeba13eb3290eebdbf88a9078d502e"}, ] [package.dependencies] @@ -668,7 +628,6 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} name = "flake8-debugger" version = "4.1.2" description = "ipdb/pdb statement checker plugin for flake8" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -684,7 +643,6 @@ pycodestyle = "*" name = "flake8-import-order" version = "0.18.2" description = "Flake8 and pylama plugin that checks the ordering of import statements." -category = "dev" optional = false python-versions = "*" files = [ @@ -700,7 +658,6 @@ setuptools = "*" name = "flake8-mutable" version = "1.2.0" description = "mutable defaults flake8 extension" -category = "dev" optional = false python-versions = "*" files = [ @@ -715,7 +672,6 @@ flake8 = "*" name = "flake8-mypy" version = "17.8.0" description = "A plugin for flake8 integrating mypy." -category = "dev" optional = false python-versions = "*" files = [] @@ -736,7 +692,6 @@ resolved_reference = "2b00b0133656af4da468a3d24935df71ffc34722" name = "flake8-plugin-utils" version = "1.3.2" description = "The package provides base classes and utils for flake8 plugin writing" -category = "dev" optional = false python-versions = ">=3.6,<4.0" files = [ @@ -748,7 +703,6 @@ files = [ name = "flake8-print" version = "5.0.0" description = "print statement checker plugin for flake8" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -764,7 +718,6 @@ pycodestyle = "*" name = "flake8-pytest-style" version = "1.6.0" description = "A flake8 plugin checking common style issues or inconsistencies with pytest-based tests." -category = "dev" optional = false python-versions = ">=3.6.2,<4.0.0" files = [ @@ -779,7 +732,6 @@ flake8-plugin-utils = ">=1.3.2,<2.0.0" name = "flake8-string-format" version = "0.3.0" description = "string format checker, plugin for flake8" -category = "dev" optional = false python-versions = "*" files = [ @@ -794,7 +746,6 @@ flake8 = "*" name = "flake8-tidy-imports" version = "4.8.0" description = "A flake8 plugin that helps you write tidier imports." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -808,27 +759,25 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "furo" -version = "2022.12.7" +version = "2023.5.20" description = "A clean customisable Sphinx documentation theme." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "furo-2022.12.7-py3-none-any.whl", hash = "sha256:7cb76c12a25ef65db85ab0743df907573d03027a33631f17d267e598ebb191f7"}, - {file = "furo-2022.12.7.tar.gz", hash = "sha256:d8008f8efbe7587a97ba533c8b2df1f9c21ee9b3e5cad0d27f61193d38b1a986"}, + {file = "furo-2023.5.20-py3-none-any.whl", hash = "sha256:594a8436ddfe0c071f3a9e9a209c314a219d8341f3f1af33fdf7c69544fab9e6"}, + {file = "furo-2023.5.20.tar.gz", hash = "sha256:40e09fa17c6f4b22419d122e933089226dcdb59747b5b6c79363089827dea16f"}, ] [package.dependencies] beautifulsoup4 = "*" pygments = ">=2.7" -sphinx = ">=5.0,<7.0" +sphinx = ">=6.0,<8.0" sphinx-basic-ng = "*" [[package]] name = "gitdb" version = "4.0.10" description = "Git Object Database" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -841,14 +790,13 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.30" -description = "GitPython is a python library used to interact with Git repositories" -category = "dev" +version = "3.1.31" +description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" files = [ - {file = "GitPython-3.1.30-py3-none-any.whl", hash = "sha256:cd455b0000615c60e286208ba540271af9fe531fa6a87cc590a7298785ab2882"}, - {file = "GitPython-3.1.30.tar.gz", hash = "sha256:769c2d83e13f5d938b7688479da374c4e3d49f71549aaf462b646db9602ea6f8"}, + {file = "GitPython-3.1.31-py3-none-any.whl", hash = "sha256:f04893614f6aa713a60cbbe1e6a97403ef633103cdd0ef5eb6efe0deb98dbe8d"}, + {file = "GitPython-3.1.31.tar.gz", hash = "sha256:8ce3bcf69adfdf7c7d503e78fd3b1c492af782d58893b650adb2ac8912ddd573"}, ] [package.dependencies] @@ -859,7 +807,6 @@ typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\"" name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -871,7 +818,6 @@ files = [ name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -881,14 +827,13 @@ files = [ [[package]] name = "importlib-metadata" -version = "6.0.0" +version = "6.6.0" description = "Read metadata from Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"}, - {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"}, + {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, + {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, ] [package.dependencies] @@ -904,7 +849,6 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -916,7 +860,6 @@ files = [ name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -930,11 +873,35 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "markdown-it-py" +version = "2.2.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.7" +files = [ + {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, + {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" +typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "markupsafe" version = "2.1.2" description = "Safely add untrusted strings to HTML/XML markup." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -994,7 +961,6 @@ files = [ name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1002,48 +968,54 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "mypy" -version = "0.991" +version = "1.3.0" description = "Optional static typing for Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "mypy-0.991-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab"}, - {file = "mypy-0.991-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d"}, - {file = "mypy-0.991-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6"}, - {file = "mypy-0.991-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb"}, - {file = "mypy-0.991-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305"}, - {file = "mypy-0.991-cp310-cp310-win_amd64.whl", hash = "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c"}, - {file = "mypy-0.991-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372"}, - {file = "mypy-0.991-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f"}, - {file = "mypy-0.991-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33"}, - {file = "mypy-0.991-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05"}, - {file = "mypy-0.991-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad"}, - {file = "mypy-0.991-cp311-cp311-win_amd64.whl", hash = "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297"}, - {file = "mypy-0.991-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813"}, - {file = "mypy-0.991-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711"}, - {file = "mypy-0.991-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd"}, - {file = "mypy-0.991-cp37-cp37m-win_amd64.whl", hash = "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef"}, - {file = "mypy-0.991-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a"}, - {file = "mypy-0.991-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93"}, - {file = "mypy-0.991-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf"}, - {file = "mypy-0.991-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135"}, - {file = "mypy-0.991-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70"}, - {file = "mypy-0.991-cp38-cp38-win_amd64.whl", hash = "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243"}, - {file = "mypy-0.991-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d"}, - {file = "mypy-0.991-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5"}, - {file = "mypy-0.991-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3"}, - {file = "mypy-0.991-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648"}, - {file = "mypy-0.991-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476"}, - {file = "mypy-0.991-cp39-cp39-win_amd64.whl", hash = "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461"}, - {file = "mypy-0.991-py3-none-any.whl", hash = "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb"}, - {file = "mypy-0.991.tar.gz", hash = "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06"}, + {file = "mypy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eb485cea53f4f5284e5baf92902cd0088b24984f4209e25981cc359d64448d"}, + {file = "mypy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c99c3ecf223cf2952638da9cd82793d8f3c0c5fa8b6ae2b2d9ed1e1ff51ba85"}, + {file = "mypy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:550a8b3a19bb6589679a7c3c31f64312e7ff482a816c96e0cecec9ad3a7564dd"}, + {file = "mypy-1.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cbc07246253b9e3d7d74c9ff948cd0fd7a71afcc2b77c7f0a59c26e9395cb152"}, + {file = "mypy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:a22435632710a4fcf8acf86cbd0d69f68ac389a3892cb23fbad176d1cddaf228"}, + {file = "mypy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6e33bb8b2613614a33dff70565f4c803f889ebd2f859466e42b46e1df76018dd"}, + {file = "mypy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d23370d2a6b7a71dc65d1266f9a34e4cde9e8e21511322415db4b26f46f6b8c"}, + {file = "mypy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:658fe7b674769a0770d4b26cb4d6f005e88a442fe82446f020be8e5f5efb2fae"}, + {file = "mypy-1.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d29e324cdda61daaec2336c42512e59c7c375340bd202efa1fe0f7b8f8ca"}, + {file = "mypy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:d0b6c62206e04061e27009481cb0ec966f7d6172b5b936f3ead3d74f29fe3dcf"}, + {file = "mypy-1.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:76ec771e2342f1b558c36d49900dfe81d140361dd0d2df6cd71b3db1be155409"}, + {file = "mypy-1.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc95f8386314272bbc817026f8ce8f4f0d2ef7ae44f947c4664efac9adec929"}, + {file = "mypy-1.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:faff86aa10c1aa4a10e1a301de160f3d8fc8703b88c7e98de46b531ff1276a9a"}, + {file = "mypy-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:8c5979d0deb27e0f4479bee18ea0f83732a893e81b78e62e2dda3e7e518c92ee"}, + {file = "mypy-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c5d2cc54175bab47011b09688b418db71403aefad07cbcd62d44010543fc143f"}, + {file = "mypy-1.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:87df44954c31d86df96c8bd6e80dfcd773473e877ac6176a8e29898bfb3501cb"}, + {file = "mypy-1.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:473117e310febe632ddf10e745a355714e771ffe534f06db40702775056614c4"}, + {file = "mypy-1.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:74bc9b6e0e79808bf8678d7678b2ae3736ea72d56eede3820bd3849823e7f305"}, + {file = "mypy-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:44797d031a41516fcf5cbfa652265bb994e53e51994c1bd649ffcd0c3a7eccbf"}, + {file = "mypy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ddae0f39ca146972ff6bb4399f3b2943884a774b8771ea0a8f50e971f5ea5ba8"}, + {file = "mypy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c4c42c60a8103ead4c1c060ac3cdd3ff01e18fddce6f1016e08939647a0e703"}, + {file = "mypy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e86c2c6852f62f8f2b24cb7a613ebe8e0c7dc1402c61d36a609174f63e0ff017"}, + {file = "mypy-1.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f9dca1e257d4cc129517779226753dbefb4f2266c4eaad610fc15c6a7e14283e"}, + {file = "mypy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:95d8d31a7713510685b05fbb18d6ac287a56c8f6554d88c19e73f724a445448a"}, + {file = "mypy-1.3.0-py3-none-any.whl", hash = "sha256:a8763e72d5d9574d45ce5881962bc8e9046bf7b375b0abf031f3e6811732a897"}, + {file = "mypy-1.3.0.tar.gz", hash = "sha256:e1f4d16e296f5135624b34e8fb741eb0eadedca90862405b1f1fde2040b9bd11"}, ] [package.dependencies] -mypy-extensions = ">=0.4.3" +mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} typing-extensions = ">=3.10" @@ -1056,45 +1028,41 @@ reports = ["lxml"] [[package]] name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." -category = "dev" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." optional = false -python-versions = "*" +python-versions = ">=3.5" files = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "packaging" -version = "23.0" +version = "23.1" description = "Core utilities for Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, - {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] [[package]] name = "pathspec" -version = "0.11.0" +version = "0.11.1" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"}, - {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, + {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, + {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, ] [[package]] name = "pbr" version = "5.11.1" description = "Python Build Reasonableness" -category = "dev" optional = false python-versions = ">=2.6" files = [ @@ -1106,7 +1074,6 @@ files = [ name = "pep8-naming" version = "0.13.3" description = "Check PEP-8 naming conventions, plugin for flake8" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1119,28 +1086,26 @@ flake8 = ">=5.0.0" [[package]] name = "platformdirs" -version = "2.6.2" +version = "3.5.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, - {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, ] [package.dependencies] -typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} +typing-extensions = {version = ">=4.5", markers = "python_version < \"3.8\""} [package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1157,26 +1122,25 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "psutil" -version = "5.9.4" +version = "5.9.5" description = "Cross-platform lib for process and system monitoring in Python." -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ - {file = "psutil-5.9.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c1ca331af862803a42677c120aff8a814a804e09832f166f226bfd22b56feee8"}, - {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:68908971daf802203f3d37e78d3f8831b6d1014864d7a85937941bb35f09aefe"}, - {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549"}, - {file = "psutil-5.9.4-cp27-cp27m-win32.whl", hash = "sha256:852dd5d9f8a47169fe62fd4a971aa07859476c2ba22c2254d4a1baa4e10b95ad"}, - {file = "psutil-5.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9120cd39dca5c5e1c54b59a41d205023d436799b1c8c4d3ff71af18535728e94"}, - {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24"}, - {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:efeae04f9516907be44904cc7ce08defb6b665128992a56957abc9b61dca94b7"}, - {file = "psutil-5.9.4-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7"}, - {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16653106f3b59386ffe10e0bad3bb6299e169d5327d3f187614b1cb8f24cf2e1"}, - {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54c0d3d8e0078b7666984e11b12b88af2db11d11249a8ac8920dd5ef68a66e08"}, - {file = "psutil-5.9.4-cp36-abi3-win32.whl", hash = "sha256:149555f59a69b33f056ba1c4eb22bb7bf24332ce631c44a319cec09f876aaeff"}, - {file = "psutil-5.9.4-cp36-abi3-win_amd64.whl", hash = "sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4"}, - {file = "psutil-5.9.4-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6001c809253a29599bc0dfd5179d9f8a5779f9dffea1da0f13c53ee568115e1e"}, - {file = "psutil-5.9.4.tar.gz", hash = "sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62"}, + {file = "psutil-5.9.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:be8929ce4313f9f8146caad4272f6abb8bf99fc6cf59344a3167ecd74f4f203f"}, + {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ab8ed1a1d77c95453db1ae00a3f9c50227ebd955437bcf2a574ba8adbf6a74d5"}, + {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4aef137f3345082a3d3232187aeb4ac4ef959ba3d7c10c33dd73763fbc063da4"}, + {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ea8518d152174e1249c4f2a1c89e3e6065941df2fa13a1ab45327716a23c2b48"}, + {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:acf2aef9391710afded549ff602b5887d7a2349831ae4c26be7c807c0a39fac4"}, + {file = "psutil-5.9.5-cp27-none-win32.whl", hash = "sha256:5b9b8cb93f507e8dbaf22af6a2fd0ccbe8244bf30b1baad6b3954e935157ae3f"}, + {file = "psutil-5.9.5-cp27-none-win_amd64.whl", hash = "sha256:8c5f7c5a052d1d567db4ddd231a9d27a74e8e4a9c3f44b1032762bd7b9fdcd42"}, + {file = "psutil-5.9.5-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217"}, + {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da"}, + {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4"}, + {file = "psutil-5.9.5-cp36-abi3-win32.whl", hash = "sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d"}, + {file = "psutil-5.9.5-cp36-abi3-win_amd64.whl", hash = "sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9"}, + {file = "psutil-5.9.5-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30"}, + {file = "psutil-5.9.5.tar.gz", hash = "sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c"}, ] [package.extras] @@ -1186,7 +1150,6 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] name = "py-cpuinfo" version = "9.0.0" description = "Get CPU info with pure Python" -category = "dev" optional = false python-versions = "*" files = [ @@ -1198,7 +1161,6 @@ files = [ name = "pycodestyle" version = "2.10.0" description = "Python style guide checker" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1210,7 +1172,6 @@ files = [ name = "pyflakes" version = "3.0.1" description = "passive checker of Python programs" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1220,14 +1181,13 @@ files = [ [[package]] name = "pygments" -version = "2.14.0" +version = "2.15.1" description = "Pygments is a syntax highlighting package written in Python." -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"}, - {file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"}, + {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, + {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, ] [package.extras] @@ -1235,29 +1195,27 @@ plugins = ["importlib-metadata"] [[package]] name = "pyproject-api" -version = "1.5.0" +version = "1.5.1" description = "API to interact with the python pyproject.toml based projects" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pyproject_api-1.5.0-py3-none-any.whl", hash = "sha256:4c111277dfb96bcd562c6245428f27250b794bfe3e210b8714c4f893952f2c17"}, - {file = "pyproject_api-1.5.0.tar.gz", hash = "sha256:0962df21f3e633b8ddb9567c011e6c1b3dcdfc31b7860c0ede7e24c5a1200fbe"}, + {file = "pyproject_api-1.5.1-py3-none-any.whl", hash = "sha256:4698a3777c2e0f6b624f8a4599131e2a25376d90fe8d146d7ac74c67c6f97c43"}, + {file = "pyproject_api-1.5.1.tar.gz", hash = "sha256:435f46547a9ff22cf4208ee274fca3e2869aeb062a4834adfc99a4dd64af3cf9"}, ] [package.dependencies] -packaging = ">=21.3" +packaging = ">=23" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [package.extras] -docs = ["furo (>=2022.9.29)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] -testing = ["covdefaults (>=2.2.2)", "importlib-metadata (>=5.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "virtualenv (>=20.17)", "wheel (>=0.38.4)"] +docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] +testing = ["covdefaults (>=2.2.2)", "importlib-metadata (>=6)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "virtualenv (>=20.17.1)", "wheel (>=0.38.4)"] [[package]] name = "pyproject-hooks" version = "1.0.0" description = "Wrappers to call pyproject.toml-based build backend hooks." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1270,18 +1228,16 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [[package]] name = "pytest" -version = "7.2.1" +version = "7.3.1" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.2.1-py3-none-any.whl", hash = "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5"}, - {file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"}, + {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, + {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, ] [package.dependencies] -attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} @@ -1291,13 +1247,12 @@ pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] [[package]] name = "pytest-benchmark" version = "4.0.0" description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1316,14 +1271,13 @@ histogram = ["pygal", "pygaljs"] [[package]] name = "pytest-cov" -version = "4.0.0" +version = "4.1.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, - {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, ] [package.dependencies] @@ -1337,7 +1291,6 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale name = "pytest-mock" version = "3.10.0" description = "Thin-wrapper around the mock package for easier use with pytest" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1355,7 +1308,6 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] name = "pytest-randomly" version = "3.12.0" description = "Pytest plugin to randomly order tests and control random.seed." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1369,14 +1321,13 @@ pytest = "*" [[package]] name = "pytest-xdist" -version = "3.1.0" +version = "3.3.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-xdist-3.1.0.tar.gz", hash = "sha256:40fdb8f3544921c5dfcd486ac080ce22870e71d82ced6d2e78fa97c2addd480c"}, - {file = "pytest_xdist-3.1.0-py3-none-any.whl", hash = "sha256:70a76f191d8a1d2d6be69fc440cdf85f3e4c03c08b520fd5dc5d338d6cf07d89"}, + {file = "pytest-xdist-3.3.1.tar.gz", hash = "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93"}, + {file = "pytest_xdist-3.3.1-py3-none-any.whl", hash = "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2"}, ] [package.dependencies] @@ -1390,21 +1341,19 @@ testing = ["filelock"] [[package]] name = "pytz" -version = "2022.7.1" +version = "2023.3" description = "World timezone definitions, modern and historical" -category = "dev" optional = false python-versions = "*" files = [ - {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, - {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, + {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, + {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, ] [[package]] name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1454,7 +1403,6 @@ files = [ name = "recommonmark" version = "0.7.1" description = "A docutils-compatibility bridge to CommonMark, enabling you to write CommonMark inside of Docutils & Sphinx projects." -category = "dev" optional = false python-versions = "*" files = [ @@ -1469,48 +1417,64 @@ sphinx = ">=1.3.1" [[package]] name = "requests" -version = "2.28.2" +version = "2.31.0" description = "Python HTTP for Humans." -category = "dev" optional = false -python-versions = ">=3.7, <4" +python-versions = ">=3.7" files = [ - {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, - {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" +urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "rich" +version = "13.4.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.4.1-py3-none-any.whl", hash = "sha256:d204aadb50b936bf6b1a695385429d192bc1fdaf3e8b907e8e26f4c4e4b5bf75"}, + {file = "rich-13.4.1.tar.gz", hash = "sha256:76f6b65ea7e5c5d924ba80e322231d7cb5b5981aa60bfc1e694f1bc097fe6fe1"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0,<3.0.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "setuptools" -version = "66.1.1" +version = "67.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-66.1.1-py3-none-any.whl", hash = "sha256:6f590d76b713d5de4e49fe4fbca24474469f53c83632d5d0fd056f7ff7e8112b"}, - {file = "setuptools-66.1.1.tar.gz", hash = "sha256:ac4008d396bc9cd983ea483cb7139c0240a07bbc74ffb6232fceffedc6cf03a8"}, + {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, + {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "smmap" version = "5.0.0" description = "A pure Python implementation of a sliding window memory map manager" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1522,7 +1486,6 @@ files = [ name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "dev" optional = false python-versions = "*" files = [ @@ -1532,33 +1495,31 @@ files = [ [[package]] name = "soupsieve" -version = "2.3.2.post1" +version = "2.4.1" description = "A modern CSS selector implementation for Beautiful Soup." -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, - {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, + {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"}, + {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, ] [[package]] name = "sphinx" -version = "6.1.3" +version = "6.2.1" description = "Python documentation generator" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "Sphinx-6.1.3.tar.gz", hash = "sha256:0dac3b698538ffef41716cf97ba26c1c7788dba73ce6f150c1ff5b4720786dd2"}, - {file = "sphinx-6.1.3-py3-none-any.whl", hash = "sha256:807d1cb3d6be87eb78a381c3e70ebd8d346b9a25f3753e9947e866b2786865fc"}, + {file = "Sphinx-6.2.1.tar.gz", hash = "sha256:6d56a34697bb749ffa0152feafc4b19836c755d90a7c59b72bc7dfd371b9cc6b"}, + {file = "sphinx-6.2.1-py3-none-any.whl", hash = "sha256:97787ff1fa3256a3eef9eda523a63dbf299f7b47e053cfcf684a1c2a8380c912"}, ] [package.dependencies] alabaster = ">=0.7,<0.8" babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.18,<0.20" +docutils = ">=0.18.1,<0.20" imagesize = ">=1.3" importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" @@ -1576,33 +1537,31 @@ sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] -test = ["cython", "html5lib", "pytest (>=4.6)"] +test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"] [[package]] name = "sphinx-autodoc-typehints" -version = "1.21.8" +version = "1.23.0" description = "Type hints (PEP 484) support for the Sphinx autodoc extension" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "sphinx_autodoc_typehints-1.21.8-py3-none-any.whl", hash = "sha256:b8d15c05ced5e9fbe46a6dfd2ff194b6e508a64c9229bc7eaa08ee7089b9e6d3"}, - {file = "sphinx_autodoc_typehints-1.21.8.tar.gz", hash = "sha256:68e7136403ca67359d7beaab4d358cea518623abe749cc4e0639609aebf2f5c7"}, + {file = "sphinx_autodoc_typehints-1.23.0-py3-none-any.whl", hash = "sha256:ac099057e66b09e51b698058ba7dd76e57e1fe696cd91b54e121d3dad188f91d"}, + {file = "sphinx_autodoc_typehints-1.23.0.tar.gz", hash = "sha256:5d44e2996633cdada499b6d27a496ddf9dbc95dd1f0f09f7b37940249e61f6e9"}, ] [package.dependencies] sphinx = ">=5.3" [package.extras] -docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.21)"] -testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.5)", "diff-cover (>=7.3)", "nptyping (>=2.4.1)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "sphobjinv (>=2.3.1)", "typing-extensions (>=4.4)"] +docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23.4)"] +testing = ["covdefaults (>=2.2.2)", "coverage (>=7.2.2)", "diff-cover (>=7.5)", "nptyping (>=2.5)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "sphobjinv (>=2.3.1)", "typing-extensions (>=4.5)"] type-comment = ["typed-ast (>=1.5.4)"] [[package]] name = "sphinx-basic-ng" version = "1.0.0b1" description = "A modern skeleton for Sphinx themes." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1618,14 +1577,13 @@ docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-ta [[package]] name = "sphinx-copybutton" -version = "0.5.1" +version = "0.5.2" description = "Add a copy button to each of your code cells." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "sphinx-copybutton-0.5.1.tar.gz", hash = "sha256:366251e28a6f6041514bfb5439425210418d6c750e98d3a695b73e56866a677a"}, - {file = "sphinx_copybutton-0.5.1-py3-none-any.whl", hash = "sha256:0842851b5955087a7ec7fc870b622cb168618ad408dee42692e9a5c97d071da8"}, + {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, + {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, ] [package.dependencies] @@ -1639,7 +1597,6 @@ rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] name = "sphinx-inline-tabs" version = "2022.1.2b11" description = "Add inline tabbed content to your Sphinx documentation." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1658,7 +1615,6 @@ test = ["pytest", "pytest-cov", "pytest-xdist"] name = "sphinx-tabs" version = "3.4.1" description = "Tabbed views for Sphinx" -category = "dev" optional = false python-versions = "~=3.7" files = [ @@ -1679,7 +1635,6 @@ testing = ["bs4", "coverage", "pygments", "pytest (>=7.1,<8)", "pytest-cov", "py name = "sphinxcontrib-applehelp" version = "1.0.4" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1695,7 +1650,6 @@ test = ["pytest"] name = "sphinxcontrib-devhelp" version = "1.0.2" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1709,14 +1663,13 @@ test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" -version = "2.0.0" +version = "2.0.1" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, - {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, + {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, + {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, ] [package.extras] @@ -1727,7 +1680,6 @@ test = ["html5lib", "pytest"] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1742,7 +1694,6 @@ test = ["flake8", "mypy", "pytest"] name = "sphinxcontrib-qthelp" version = "1.0.3" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1758,7 +1709,6 @@ test = ["pytest"] name = "sphinxcontrib-serializinghtml" version = "1.1.5" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -1774,7 +1724,6 @@ test = ["pytest"] name = "stevedore" version = "3.5.2" description = "Manage dynamic plugins for Python applications" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1790,7 +1739,6 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0" name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1802,7 +1750,6 @@ files = [ name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1812,44 +1759,42 @@ files = [ [[package]] name = "tox" -version = "4.4.0" +version = "4.5.2" description = "tox is a generic virtualenv management and test command line tool" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "tox-4.4.0-py3-none-any.whl", hash = "sha256:a9aaef6ee45714bcc28c99aa91b6a8c098eca6b67d361c6d3ea033a82acfca22"}, - {file = "tox-4.4.0.tar.gz", hash = "sha256:597d9e55e2b476e5b92cfedea7d4132b0ab90e6a23ba93774f47763239545d75"}, + {file = "tox-4.5.2-py3-none-any.whl", hash = "sha256:f1a9541b292aa0449f6c7bb67dc0073f25f9086413c3922fe47f5168cbf7b2f4"}, + {file = "tox-4.5.2.tar.gz", hash = "sha256:ad87fb7a10ef476afb6eb7e408808057f42976ef0d30ad5fe023099ba493ce58"}, ] [package.dependencies] -cachetools = ">=5.2.1" +cachetools = ">=5.3" chardet = ">=5.1" colorama = ">=0.4.6" -filelock = ">=3.9" -importlib-metadata = {version = ">=6", markers = "python_version < \"3.8\""} -packaging = ">=23" -platformdirs = ">=2.6.2" +filelock = ">=3.12" +importlib-metadata = {version = ">=6.6", markers = "python_version < \"3.8\""} +packaging = ">=23.1" +platformdirs = ">=3.5.1" pluggy = ">=1" -pyproject-api = ">=1.5" +pyproject-api = ">=1.5.1" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""} -virtualenv = ">=20.17.1" +typing-extensions = {version = ">=4.6.2", markers = "python_version < \"3.8\""} +virtualenv = ">=20.23" [package.extras] -docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-argparse-cli (>=1.11)", "sphinx-autodoc-typehints (>=1.20.1)", "sphinx-copybutton (>=0.5.1)", "sphinx-inline-tabs (>=2022.1.2b11)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -testing = ["build[virtualenv] (>=0.9)", "covdefaults (>=2.2.2)", "devpi-process (>=0.3)", "diff-cover (>=7.3)", "distlib (>=0.3.6)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.12.2)", "psutil (>=5.9.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-xdist (>=3.1)", "re-assert (>=1.1)", "time-machine (>=2.8.2)", "wheel (>=0.38.4)"] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-argparse-cli (>=1.11)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "devpi-process (>=0.3)", "diff-cover (>=7.5)", "distlib (>=0.3.6)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.17)", "psutil (>=5.9.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-xdist (>=3.3.1)", "re-assert (>=1.1)", "time-machine (>=2.9)", "wheel (>=0.40)"] [[package]] name = "tox-gh-actions" -version = "3.0.0" +version = "3.1.1" description = "Seamless integration of tox into GitHub Actions" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "tox-gh-actions-3.0.0.tar.gz", hash = "sha256:1a20286a1102c720a5979f56a060f6d618306dbb660a6724a31d89f397421e5c"}, - {file = "tox_gh_actions-3.0.0-py2.py3-none-any.whl", hash = "sha256:059ebfdba76864819f3a4629c3404be094dae9dc8e7922c7c7a9999b9551f82a"}, + {file = "tox-gh-actions-3.1.1.tar.gz", hash = "sha256:8092fbedef53d29ca4f5ee35e15b4324b2edcd5c9dc17db56777cddac23ac3c1"}, + {file = "tox_gh_actions-3.1.1-py2.py3-none-any.whl", hash = "sha256:06cbe70c80eb36c560df6e437c94a1782b894b1f7cc3afdd8cf85cdf62701bde"}, ] [package.dependencies] @@ -1862,7 +1807,6 @@ testing = ["black", "devpi-process", "flake8 (>=6,<7)", "mypy", "pytest (>=7,<8) name = "typed-ast" version = "1.5.4" description = "a fork of Python 2 and 3 ast modules with type comment support" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1894,43 +1838,41 @@ files = [ [[package]] name = "typing-extensions" -version = "4.4.0" +version = "4.6.3" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, - {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, + {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, + {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, ] [[package]] name = "urllib3" -version = "1.26.14" +version = "2.0.2" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7" files = [ - {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"}, - {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, + {file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"}, + {file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "validate-pyproject" -version = "0.12" +version = "0.13" description = "Validation library and CLI tool for checking on 'pyproject.toml' files using JSON Schema" -category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "validate-pyproject-0.12.tar.gz", hash = "sha256:1a87b19b0db585e70c2053f8e8eaa586f1fae342b2387c8dc92b4ce776a64508"}, - {file = "validate_pyproject-0.12-py3-none-any.whl", hash = "sha256:517fb56c32f7d3c139678dd897d7bed7f83c157ef637e5969ad88c4ebc38dc89"}, + {file = "validate-pyproject-0.13.tar.gz", hash = "sha256:9fc9153ea587592a37ed5313628221bd276886497cb8ec35a2e02c28ddd39911"}, + {file = "validate_pyproject-0.13-py3-none-any.whl", hash = "sha256:5026aabfb376744032e5f9cdfceb4e75badce181b21398f64a768c373531b3e0"}, ] [package.dependencies] @@ -1944,31 +1886,29 @@ typecheck = ["importlib-resources", "mypy"] [[package]] name = "virtualenv" -version = "20.17.1" +version = "20.23.0" description = "Virtual Python Environment builder" -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "virtualenv-20.17.1-py3-none-any.whl", hash = "sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4"}, - {file = "virtualenv-20.17.1.tar.gz", hash = "sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"}, + {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, + {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, ] [package.dependencies] distlib = ">=0.3.6,<1" -filelock = ">=3.4.1,<4" -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""} -platformdirs = ">=2.4,<3" +filelock = ">=3.11,<4" +importlib-metadata = {version = ">=6.4.1", markers = "python_version < \"3.8\""} +platformdirs = ">=3.2,<4" [package.extras] -docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] -testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] [[package]] name = "vulture" version = "2.7" description = "Find dead code" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1981,19 +1921,18 @@ toml = "*" [[package]] name = "zipp" -version = "3.11.0" +version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, - {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "2.0" From c2655e0521ffcb4a8f5905608ccdb63d924e723b Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 1 Jun 2023 21:36:00 -0600 Subject: [PATCH 016/112] lint fixes --- getmac/getmac.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/getmac/getmac.py b/getmac/getmac.py index 10d7167..da94202 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -581,10 +581,8 @@ def get(self, arg): # type: (str) -> Optional[str] return self._call_habets(arg) except CalledProcessError as ex: if ex.output and self._is_iputils: - if not PY2 and isinstance(ex.output, bytes): - output = str(ex.output, "utf-8").lower() - else: - output = str(ex.output).lower() + if isinstance(ex.output, bytes): + output = ex.output.decode("utf-8").lower() if "habets" in output or "invalid option" in output: if DEBUG: @@ -1363,7 +1361,7 @@ def _swap_method_fallback(method_type, swap_with): def _warn_critical(err_msg): # type: (str) -> None log.critical(err_msg) - warnings.warn( + warnings.warn( # noqa: B028 "%s. NOTICE: this warning will likely turn into a raised exception in getmac 1.0.0!" % err_msg, RuntimeWarning, @@ -1442,7 +1440,7 @@ def initialize_method_cache( "Falling back to platform 'other'." % platform ) log.warning(warn_msg) - warnings.warn(warn_msg, RuntimeWarning) + warnings.warn(warn_msg, RuntimeWarning, stacklevel=2) platform_methods = [ method for method in type_methods if "other" in method.platforms ] From 2f002dede02c09c8006e124c7ac8c47edb7a08cd Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 00:38:01 -0600 Subject: [PATCH 017/112] Move to ruff Bye-bye flake8. Your stubbornness on supporting pyproject.toml leaves a bad taste in my mouth, but otherwise it's been good times. --- .gitignore | 1 + getmac/__init__.py | 2 +- getmac/__main__.py | 2 +- getmac/getmac.py | 67 +++-- poetry.lock | 650 +++++-------------------------------------- pyproject.toml | 113 +++++--- tests/test_cli.py | 32 +-- tests/test_getmac.py | 3 +- tox.ini | 24 +- 9 files changed, 206 insertions(+), 688 deletions(-) diff --git a/.gitignore b/.gitignore index 254a628..47b3c9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .idea/ .vscode/ .vagrant/ +.pdm-python .pytest_cache diff --git a/getmac/__init__.py b/getmac/__init__.py index d0e9a04..ff8dd56 100644 --- a/getmac/__init__.py +++ b/getmac/__init__.py @@ -1,3 +1,3 @@ -from .getmac import __version__, get_mac_address +from .getmac import __version__, get_mac_address # noqa: F401 __all__ = ["get_mac_address"] diff --git a/getmac/__main__.py b/getmac/__main__.py index 613795e..0e5b1b8 100644 --- a/getmac/__main__.py +++ b/getmac/__main__.py @@ -110,7 +110,7 @@ def main(): # type: () -> None ) if mac is not None: - print(mac) # noqa: T001, T201 + print(mac) # noqa: T201 sys.exit(0) # Exit success! else: sys.exit(1) # Exit with error since it failed to find a MAC diff --git a/getmac/getmac.py b/getmac/getmac.py index da94202..31782b6 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -41,7 +41,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Dict, List, Optional, Set, Tuple, Type, Union + from typing import Dict, List, Optional, Set, Tuple, Type, Union # noqa: F401 except ImportError: pass @@ -201,7 +201,7 @@ def _read_file(filepath): try: with open(filepath) as f: return f.read() - except OSError: # noqa: B014 + except OSError: log.debug("Could not find file: '%s'", filepath) return None @@ -244,9 +244,11 @@ def _call_proc(executable, args): if WINDOWS: cmd = executable + " " + args # type: ignore else: - cmd = [executable] + shlex.split(args) # type: ignore + cmd = [executable, *shlex.split(args)] # type: ignore - output = check_output(cmd, stderr=DEVNULL, env=ENV) # type: Union[bytes, str] + output = check_output( + cmd, stderr=DEVNULL, env=ENV # noqa: S603 + ) # type: Union[bytes, str] if DEBUG >= 4: log.debug("Output from '%s' command: %s", executable, str(output)) @@ -304,9 +306,9 @@ class Method: #: error indicating the method won't work on the current platform. unusable = False # type: bool - def test(self): # type: () -> bool # noqa: T484 + def test(self): # type: () -> bool """Low-impact test that the method is feasible, e.g. command exists.""" - pass # pragma: no cover + return False # pragma: no cover # TODO: automatically clean MAC on return def get(self, arg): # type: (str) -> Optional[str] @@ -342,7 +344,7 @@ class UuidArpGetNode(Method): def test(self): # type: () -> bool try: - from uuid import _arp_getnode # type: ignore + from uuid import _arp_getnode # type: ignore # noqa: F401 return True except Exception: @@ -353,7 +355,7 @@ def get(self, arg): # type: (str) -> Optional[str] backup = socket.gethostbyname try: - socket.gethostbyname = lambda x: arg # noqa: F841 + socket.gethostbyname = lambda x: arg # noqa: ARG005 mac1 = _arp_getnode() if mac1 is not None: mac1 = _uuid_convert(mac1) @@ -509,7 +511,7 @@ class ArpExe(Method): This only works for IPv4, since the ARP table is IPv4-only. Microsoft Documentation: `arp ` - """ # noqa: E501 + """ platforms = {"windows", "wsl"} method_type = "ip4" @@ -609,7 +611,7 @@ class CtypesHost(Method): of a remote IPv4 host. Microsoft Documentation: `SendARP function (iphlpapi.h) `__ - """ # noqa: E501 + """ platforms = {"windows"} method_type = "ip4" @@ -617,7 +619,7 @@ class CtypesHost(Method): def test(self): # type: () -> bool try: - return ctypes.windll.wsock32.inet_addr(b"127.0.0.1") > 0 # noqa: T484 + return ctypes.windll.wsock32.inet_addr(b"127.0.0.1") > 0 except Exception: return False @@ -702,7 +704,7 @@ class UuidLanscan(Method): def test(self): # type: () -> bool try: - from uuid import _find_mac # noqa: T484 + from uuid import _find_mac # type: ignore # noqa: F401 return check_command("lanscan") except Exception: @@ -713,7 +715,7 @@ def get(self, arg): # type: (str) -> Optional[str] arg = bytes(arg, "utf-8") # type: ignore - mac = _find_mac("lanscan", "-ai", [arg], lambda i: 0) + mac = _find_mac("lanscan", "-ai", [arg], lambda i: 0) # noqa: ARG005 if mac: return _uuid_convert(mac) @@ -727,7 +729,7 @@ class FcntlIface(Method): def test(self): # type: () -> bool try: - import fcntl + import fcntl # noqa: F401 return True except Exception: # Broad except to handle unknown effects @@ -741,7 +743,7 @@ def get(self, arg): # type: (str) -> Optional[str] s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 0x8927 = SIOCGIFADDR - info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack("256s", arg[:15])) + info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack("256s", arg[:15])) # type: ignore return ":".join(["%02x" % ord(chr(char)) for char in info[18:24]]) @@ -751,7 +753,7 @@ class GetmacExe(Method): Uses Windows-builtin ``getmac.exe`` to get a interface's MAC address. Microsoft Documentation: `getmac `__ - """ # noqa: E501 + """ platforms = {"windows"} method_type = "iface" @@ -800,7 +802,7 @@ class IpconfigExe(Method): versions and releases. I'm not sure if it works pre-XP though. Microsoft Documentation: `ipconfig `__ - """ # noqa: E501 + """ platforms = {"windows"} method_type = "iface" @@ -825,7 +827,7 @@ class WmicExe(Method): WMIC is deprecated as of Windows 10 21H1. This method may not work on Windows 11 and may stop working at some point on Windows 10 (unlikely, but possible). - """ # noqa: E501 + """ platforms = {"windows"} method_type = "iface" @@ -988,7 +990,7 @@ def get(self, arg): # type: (str) -> Optional[str] for pair_to_test in self._args: try: command_output = _popen("ifconfig", pair_to_test[0]) - self._good_pair = list(pair_to_test) # noqa: T484 + self._good_pair = list(pair_to_test) # type: ignore if isinstance(self._good_pair[1], str): self._good_pair[1] += MAC_RE_COLON break @@ -1134,7 +1136,7 @@ class DefaultIfaceLinuxRouteFile(Method): def test(self): # type: () -> bool return check_path("/proc/net/route") - def get(self, arg=""): # type: (str) -> Optional[str] + def get(self, arg=""): # type: (str) -> Optional[str] # noqa: ARG002 data = _read_file("/proc/net/route") if data is not None and len(data) > 1: @@ -1176,7 +1178,11 @@ def get(self, arg=""): # type: (str) -> Optional[str] output = _popen("route", "-n") try: - return output.partition("0.0.0.0")[2].partition("\n")[0].split()[-1] + return ( + output.partition("0.0.0.0")[2] # noqa: S104 + .partition("\n")[0] + .split()[-1] + ) except IndexError as ex: # index errors means no default route in output? log.debug("DefaultIfaceRouteCommand failed for %s: %s", arg, str(ex)) return None @@ -1211,7 +1217,7 @@ class DefaultIfaceIpRoute(Method): def test(self): # type: () -> bool return check_command("ip") - def get(self, arg=""): # type: (str) -> Optional[str] + def get(self, arg=""): # type: (str) -> Optional[str] # noqa: ARG002 output = _popen("ip", "route list 0/0") if not output: @@ -1229,7 +1235,7 @@ class DefaultIfaceOpenBsd(Method): def test(self): # type: () -> bool return check_command("route") - def get(self, arg=""): # type: (str) -> Optional[str] + def get(self, arg=""): # type: (str) -> Optional[str] # noqa: ARG002 output = _popen("route", "-nq show -inet -gateway -priority 1") return output.partition("127.0.0.1")[0].strip().rpartition(" ")[2] @@ -1241,7 +1247,7 @@ class DefaultIfaceFreeBsd(Method): def test(self): # type: () -> bool return check_command("netstat") - def get(self, arg=""): # type: (str) -> Optional[str] + def get(self, arg=""): # type: (str) -> Optional[str] # noqa: ARG002 output = _popen("netstat", "-r") return _search(r"default[ ]+\S+[ ]+\S+[ ]+(\S+)[\r\n]+", output) @@ -1353,7 +1359,7 @@ def _swap_method_fallback(method_type, swap_with): curr = METHOD_CACHE[method_type] FALLBACK_CACHE[method_type].remove(found) METHOD_CACHE[method_type] = found - FALLBACK_CACHE[method_type].insert(0, curr) # noqa: T484 + FALLBACK_CACHE[method_type].insert(0, curr) # type: ignore return True @@ -1502,7 +1508,7 @@ def initialize_method_cache( # Populate fallback cache with all the tested methods, minus the currently active method if METHOD_CACHE[method_type] and METHOD_CACHE[method_type] in tested_methods: - tested_methods.remove(METHOD_CACHE[method_type]) # noqa: T484 + tested_methods.remove(METHOD_CACHE[method_type]) # type: ignore FALLBACK_CACHE[method_type] = tested_methods @@ -1663,7 +1669,7 @@ def get_by_method(method_type, arg="", network_request=True): return result -def get_mac_address( # noqa: C901 +def get_mac_address( interface=None, ip=None, ip6=None, hostname=None, network_request=True ): # type: (Optional[str], Optional[str], Optional[str], Optional[str], bool) -> Optional[str] @@ -1708,7 +1714,7 @@ def get_mac_address( # noqa: C901 Returns: Lowercase colon-separated MAC address, or :obj:`None` if one could not be found or there was an error. - """ # noqa: E501 + """ if DEBUG: import timeit @@ -1774,7 +1780,8 @@ def get_mac_address( # noqa: C901 if arp_meth == str(METHOD_CACHE["ip4"]): send_udp_packet = False break - elif any( + + if any( arp_meth == str(x) for x in FALLBACK_CACHE["ip4"] ) and _swap_method_fallback("ip4", arp_meth): send_udp_packet = False @@ -1832,7 +1839,7 @@ def get_mac_address( # noqa: C901 global DEFAULT_IFACE if not DEFAULT_IFACE: - DEFAULT_IFACE = get_by_method("default_iface") # noqa: T484 + DEFAULT_IFACE = get_by_method("default_iface") # type: ignore if DEFAULT_IFACE: DEFAULT_IFACE = str(DEFAULT_IFACE).strip() diff --git a/poetry.lock b/poetry.lock index 8fb3fdc..69a6acb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -11,27 +11,6 @@ files = [ {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, ] -[[package]] -name = "attrs" -version = "23.1.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, -] - -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] - [[package]] name = "babel" version = "2.12.1" @@ -46,29 +25,6 @@ files = [ [package.dependencies] pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} -[[package]] -name = "bandit" -version = "1.7.5" -description = "Security oriented static analyser for python code." -optional = false -python-versions = ">=3.7" -files = [ - {file = "bandit-1.7.5-py3-none-any.whl", hash = "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549"}, - {file = "bandit-1.7.5.tar.gz", hash = "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -GitPython = ">=1.0.1" -PyYAML = ">=5.3.1" -rich = "*" -stevedore = ">=1.20.0" - -[package.extras] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"] -toml = ["tomli (>=1.1.0)"] -yaml = ["PyYAML"] - [[package]] name = "beautifulsoup4" version = "4.12.2" @@ -89,31 +45,45 @@ lxml = ["lxml"] [[package]] name = "black" -version = "22.12.0" +version = "23.3.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.7" files = [ - {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, - {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, - {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, - {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, - {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, - {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, - {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, - {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, - {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, - {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, - {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, - {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, + {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, + {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, + {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, + {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, + {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, + {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, + {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, + {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, + {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, + {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, + {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, + {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, + {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, ] [package.dependencies] click = ">=8.0.0" mypy-extensions = ">=0.4.3" +packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} @@ -123,30 +93,6 @@ d = ["aiohttp (>=3.7.4)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] -[[package]] -name = "build" -version = "0.10.0" -description = "A simple, correct Python build frontend" -optional = false -python-versions = ">= 3.7" -files = [ - {file = "build-0.10.0-py3-none-any.whl", hash = "sha256:af266720050a66c893a6096a2f410989eeac74ff9a68ba194b3f6473e8e26171"}, - {file = "build-0.10.0.tar.gz", hash = "sha256:d5b71264afdb5951d6704482aac78de887c80691c52b88a9ad195983ca2c9269"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "os_name == \"nt\""} -importlib-metadata = {version = ">=0.22", markers = "python_version < \"3.8\""} -packaging = ">=19.0" -pyproject_hooks = "*" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - -[package.extras] -docs = ["furo (>=2021.08.31)", "sphinx (>=4.0,<5.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)"] -test = ["filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "toml (>=0.10.0)", "wheel (>=0.36.0)"] -typing = ["importlib-metadata (>=5.1)", "mypy (==0.991)", "tomli", "typing-extensions (>=3.7.4.3)"] -virtualenv = ["virtualenv (>=20.0.35)"] - [[package]] name = "cachetools" version = "5.3.1" @@ -264,25 +210,6 @@ files = [ {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, ] -[[package]] -name = "check-manifest" -version = "0.49" -description = "Check MANIFEST.in in a Python source package for completeness" -optional = false -python-versions = ">=3.7" -files = [ - {file = "check-manifest-0.49.tar.gz", hash = "sha256:64a640445542cf226919657c7b78d02d9c1ca5b1c25d7e66e0e1ff325060f416"}, - {file = "check_manifest-0.49-py3-none-any.whl", hash = "sha256:058cd30057714c39b96ce4d83f254fc770e3145c7b1932b5940b4e3efb5521ef"}, -] - -[package.dependencies] -build = ">=0.1" -setuptools = "*" -tomli = {version = "*", markers = "python_version < \"3.11\""} - -[package.extras] -test = ["mock (>=3.0.0)", "pytest"] - [[package]] name = "click" version = "8.1.3" @@ -513,250 +440,6 @@ files = [ docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] -[[package]] -name = "flake8" -version = "6.0.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-6.0.0-py2.py3-none-any.whl", hash = "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7"}, - {file = "flake8-6.0.0.tar.gz", hash = "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.10.0,<2.11.0" -pyflakes = ">=3.0.0,<3.1.0" - -[[package]] -name = "flake8-annotations" -version = "2.9.1" -description = "Flake8 Type Annotation Checks" -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "flake8-annotations-2.9.1.tar.gz", hash = "sha256:11f09efb99ae63c8f9d6b492b75fe147fbc323179fddfe00b2e56eefeca42f57"}, - {file = "flake8_annotations-2.9.1-py3-none-any.whl", hash = "sha256:a4385158a7a9fc8af1d8820a2f4c8d03387997006a83f5f8bfe5bc6085bdf88a"}, -] - -[package.dependencies] -attrs = ">=21.4" -flake8 = ">=3.7" -typed-ast = {version = ">=1.4,<2.0", markers = "python_version < \"3.8\""} - -[[package]] -name = "flake8-black" -version = "0.3.6" -description = "flake8 plugin to call black as a code style validator" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-black-0.3.6.tar.gz", hash = "sha256:0dfbca3274777792a5bcb2af887a4cad72c72d0e86c94e08e3a3de151bb41c34"}, - {file = "flake8_black-0.3.6-py3-none-any.whl", hash = "sha256:fe8ea2eca98d8a504f22040d9117347f6b367458366952862ac3586e7d4eeaca"}, -] - -[package.dependencies] -black = ">=22.1.0" -flake8 = ">=3" -tomli = {version = "*", markers = "python_version < \"3.11\""} - -[package.extras] -develop = ["build", "twine"] - -[[package]] -name = "flake8-blind-except" -version = "0.2.1" -description = "A flake8 extension that checks for blind except: statements" -optional = false -python-versions = "*" -files = [ - {file = "flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16"}, -] - -[[package]] -name = "flake8-bugbear" -version = "23.3.12" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-bugbear-23.3.12.tar.gz", hash = "sha256:e3e7f74c8a49ad3794a7183353026dabd68c74030d5f46571f84c1fb0eb79363"}, - {file = "flake8_bugbear-23.3.12-py3-none-any.whl", hash = "sha256:beb5c7efcd7ccc2039ef66a77bb8db925e7be3531ff1cb4d0b7030d0e2113d72"}, -] - -[package.dependencies] -attrs = ">=19.2.0" -flake8 = ">=3.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] - -[[package]] -name = "flake8-builtins" -version = "2.1.0" -description = "Check for python builtins being used as variables or parameters." -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-builtins-2.1.0.tar.gz", hash = "sha256:12ff1ee96dd4e1f3141141ee6c45a5c7d3b3c440d0949e9b8d345c42b39c51d4"}, - {file = "flake8_builtins-2.1.0-py3-none-any.whl", hash = "sha256:469e8f03d6d0edf4b1e62b6d5a97dce4598592c8a13ec8f0952e7a185eba50a1"}, -] - -[package.dependencies] -flake8 = "*" - -[package.extras] -test = ["pytest"] - -[[package]] -name = "flake8-comprehensions" -version = "3.12.0" -description = "A flake8 plugin to help you write better list/set/dict comprehensions." -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8_comprehensions-3.12.0-py3-none-any.whl", hash = "sha256:013234637ec7dfcb7cd2900578fb53c512f81db909cefe371c019232695c362d"}, - {file = "flake8_comprehensions-3.12.0.tar.gz", hash = "sha256:419ef1a6e8de929203791a5e8ff5e3906caeba13eb3290eebdbf88a9078d502e"}, -] - -[package.dependencies] -flake8 = ">=3.0,<3.2.0 || >3.2.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[[package]] -name = "flake8-debugger" -version = "4.1.2" -description = "ipdb/pdb statement checker plugin for flake8" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, - {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, -] - -[package.dependencies] -flake8 = ">=3.0" -pycodestyle = "*" - -[[package]] -name = "flake8-import-order" -version = "0.18.2" -description = "Flake8 and pylama plugin that checks the ordering of import statements." -optional = false -python-versions = "*" -files = [ - {file = "flake8-import-order-0.18.2.tar.gz", hash = "sha256:e23941f892da3e0c09d711babbb0c73bc735242e9b216b726616758a920d900e"}, - {file = "flake8_import_order-0.18.2-py2.py3-none-any.whl", hash = "sha256:82ed59f1083b629b030ee9d3928d9e06b6213eb196fe745b3a7d4af2168130df"}, -] - -[package.dependencies] -pycodestyle = "*" -setuptools = "*" - -[[package]] -name = "flake8-mutable" -version = "1.2.0" -description = "mutable defaults flake8 extension" -optional = false -python-versions = "*" -files = [ - {file = "flake8-mutable-1.2.0.tar.gz", hash = "sha256:ee9b77111b867d845177bbc289d87d541445ffcc6029a0c5c65865b42b18c6a6"}, - {file = "flake8_mutable-1.2.0-py2-none-any.whl", hash = "sha256:38fd9dadcbcda6550a916197bc40ed76908119dabb37fbcca30873666c31d2d5"}, -] - -[package.dependencies] -flake8 = "*" - -[[package]] -name = "flake8-mypy" -version = "17.8.0" -description = "A plugin for flake8 integrating mypy." -optional = false -python-versions = "*" -files = [] -develop = false - -[package.dependencies] -attrs = "*" -flake8 = ">=3.0.0" -mypy = "*" - -[package.source] -type = "git" -url = "https://github.com/ambv/flake8-mypy.git" -reference = "HEAD" -resolved_reference = "2b00b0133656af4da468a3d24935df71ffc34722" - -[[package]] -name = "flake8-plugin-utils" -version = "1.3.2" -description = "The package provides base classes and utils for flake8 plugin writing" -optional = false -python-versions = ">=3.6,<4.0" -files = [ - {file = "flake8-plugin-utils-1.3.2.tar.gz", hash = "sha256:20fa2a8ca2decac50116edb42e6af0a1253ef639ad79941249b840531889c65a"}, - {file = "flake8_plugin_utils-1.3.2-py3-none-any.whl", hash = "sha256:1fe43e3e9acf3a7c0f6b88f5338cad37044d2f156c43cb6b080b5f9da8a76f06"}, -] - -[[package]] -name = "flake8-print" -version = "5.0.0" -description = "print statement checker plugin for flake8" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-print-5.0.0.tar.gz", hash = "sha256:76915a2a389cc1c0879636c219eb909c38501d3a43cc8dae542081c9ba48bdf9"}, - {file = "flake8_print-5.0.0-py3-none-any.whl", hash = "sha256:84a1a6ea10d7056b804221ac5e62b1cee1aefc897ce16f2e5c42d3046068f5d8"}, -] - -[package.dependencies] -flake8 = ">=3.0" -pycodestyle = "*" - -[[package]] -name = "flake8-pytest-style" -version = "1.6.0" -description = "A flake8 plugin checking common style issues or inconsistencies with pytest-based tests." -optional = false -python-versions = ">=3.6.2,<4.0.0" -files = [ - {file = "flake8-pytest-style-1.6.0.tar.gz", hash = "sha256:c1175713e9e11b78cd1a035ed0bca0d1e41d09c4af329a952750b61d4194ddac"}, - {file = "flake8_pytest_style-1.6.0-py3-none-any.whl", hash = "sha256:5fedb371a950e9fe0e0e6bfc854be7d99151271208f34cd2cc517681ece27780"}, -] - -[package.dependencies] -flake8-plugin-utils = ">=1.3.2,<2.0.0" - -[[package]] -name = "flake8-string-format" -version = "0.3.0" -description = "string format checker, plugin for flake8" -optional = false -python-versions = "*" -files = [ - {file = "flake8-string-format-0.3.0.tar.gz", hash = "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2"}, - {file = "flake8_string_format-0.3.0-py2.py3-none-any.whl", hash = "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af"}, -] - -[package.dependencies] -flake8 = "*" - -[[package]] -name = "flake8-tidy-imports" -version = "4.8.0" -description = "A flake8 plugin that helps you write tidier imports." -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-tidy-imports-4.8.0.tar.gz", hash = "sha256:df44f9c841b5dfb3a7a1f0da8546b319d772c2a816a1afefcce43e167a593d83"}, - {file = "flake8_tidy_imports-4.8.0-py3-none-any.whl", hash = "sha256:25bd9799358edefa0e010ce2c587b093c3aba942e96aeaa99b6d0500ae1bf09c"}, -] - -[package.dependencies] -flake8 = ">=3.8.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - [[package]] name = "furo" version = "2023.5.20" @@ -774,35 +457,6 @@ pygments = ">=2.7" sphinx = ">=6.0,<8.0" sphinx-basic-ng = "*" -[[package]] -name = "gitdb" -version = "4.0.10" -description = "Git Object Database" -optional = false -python-versions = ">=3.7" -files = [ - {file = "gitdb-4.0.10-py3-none-any.whl", hash = "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"}, - {file = "gitdb-4.0.10.tar.gz", hash = "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a"}, -] - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.31" -description = "GitPython is a Python library used to interact with Git repositories" -optional = false -python-versions = ">=3.7" -files = [ - {file = "GitPython-3.1.31-py3-none-any.whl", hash = "sha256:f04893614f6aa713a60cbbe1e6a97403ef633103cdd0ef5eb6efe0deb98dbe8d"}, - {file = "GitPython-3.1.31.tar.gz", hash = "sha256:8ce3bcf69adfdf7c7d503e78fd3b1c492af782d58893b650adb2ac8912ddd573"}, -] - -[package.dependencies] -gitdb = ">=4.0.1,<5" -typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""} - [[package]] name = "idna" version = "3.4" @@ -873,31 +527,6 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] -[[package]] -name = "markdown-it-py" -version = "2.2.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -python-versions = ">=3.7" -files = [ - {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, - {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, -] - -[package.dependencies] -mdurl = ">=0.1,<1.0" -typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - [[package]] name = "markupsafe" version = "2.1.2" @@ -957,28 +586,6 @@ files = [ {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, ] -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - [[package]] name = "mypy" version = "1.3.0" @@ -1059,31 +666,6 @@ files = [ {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, ] -[[package]] -name = "pbr" -version = "5.11.1" -description = "Python Build Reasonableness" -optional = false -python-versions = ">=2.6" -files = [ - {file = "pbr-5.11.1-py2.py3-none-any.whl", hash = "sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b"}, - {file = "pbr-5.11.1.tar.gz", hash = "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"}, -] - -[[package]] -name = "pep8-naming" -version = "0.13.3" -description = "Check PEP-8 naming conventions, plugin for flake8" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, - {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, -] - -[package.dependencies] -flake8 = ">=5.0.0" - [[package]] name = "platformdirs" version = "3.5.1" @@ -1157,28 +739,6 @@ files = [ {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, ] -[[package]] -name = "pycodestyle" -version = "2.10.0" -description = "Python style guide checker" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, - {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, -] - -[[package]] -name = "pyflakes" -version = "3.0.1" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pyflakes-3.0.1-py2.py3-none-any.whl", hash = "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf"}, - {file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"}, -] - [[package]] name = "pygments" version = "2.15.1" @@ -1212,20 +772,6 @@ tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] testing = ["covdefaults (>=2.2.2)", "importlib-metadata (>=6)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "virtualenv (>=20.17.1)", "wheel (>=0.38.4)"] -[[package]] -name = "pyproject-hooks" -version = "1.0.0" -description = "Wrappers to call pyproject.toml-based build backend hooks." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyproject_hooks-1.0.0-py3-none-any.whl", hash = "sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8"}, - {file = "pyproject_hooks-1.0.0.tar.gz", hash = "sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5"}, -] - -[package.dependencies] -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - [[package]] name = "pytest" version = "7.3.1" @@ -1350,55 +896,6 @@ files = [ {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, ] -[[package]] -name = "pyyaml" -version = "6.0" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] - [[package]] name = "recommonmark" version = "0.7.1" @@ -1437,49 +934,29 @@ socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] -name = "rich" -version = "13.4.1" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "rich-13.4.1-py3-none-any.whl", hash = "sha256:d204aadb50b936bf6b1a695385429d192bc1fdaf3e8b907e8e26f4c4e4b5bf75"}, - {file = "rich-13.4.1.tar.gz", hash = "sha256:76f6b65ea7e5c5d924ba80e322231d7cb5b5981aa60bfc1e694f1bc097fe6fe1"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0,<3.0.0" -pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - -[[package]] -name = "setuptools" -version = "67.8.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" +name = "ruff" +version = "0.0.270" +description = "An extremely fast Python linter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, - {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "smmap" -version = "5.0.0" -description = "A pure Python implementation of a sliding window memory map manager" -optional = false -python-versions = ">=3.6" -files = [ - {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, - {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, + {file = "ruff-0.0.270-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:f74c4d550f7b8e808455ac77bbce38daafc458434815ba0bc21ae4bdb276509b"}, + {file = "ruff-0.0.270-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:643de865fd35cb76c4f0739aea5afe7b8e4d40d623df7e9e6ea99054e5cead0a"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca02e709b3308eb7255b5f74e779be23b5980fca3862eae28bb23069cd61ae4"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3ed3b198768d2b3a2300fb18f730cd39948a5cc36ba29ae9d4639a11040880be"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:739495d2dbde87cf4e3110c8d27bc20febf93112539a968a4e02c26f0deccd1d"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:08188f8351f4c0b6216e8463df0a76eb57894ca59a3da65e4ed205db980fd3ae"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0827b074635d37984fc98d99316bfab5c8b1231bb83e60dacc83bd92883eedb4"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d61ae4841313f6eeb8292dc349bef27b4ce426e62c36e80ceedc3824e408734"}, + {file = "ruff-0.0.270-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0eb412f20e77529a01fb94d578b19dcb8331b56f93632aa0cce4a2ea27b7aeba"}, + {file = "ruff-0.0.270-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b775e2c5fc869359daf8c8b8aa0fd67240201ab2e8d536d14a0edf279af18786"}, + {file = "ruff-0.0.270-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:21f00e47ab2308617c44435c8dfd9e2e03897461c9e647ec942deb2a235b4cfd"}, + {file = "ruff-0.0.270-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0bbfbf6fd2436165566ca85f6e57be03ed2f0a994faf40180cfbb3604c9232ef"}, + {file = "ruff-0.0.270-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8af391ef81f7be960be10886a3c1aac0b298bde7cb9a86ec2b05faeb2081ce6b"}, + {file = "ruff-0.0.270-py3-none-win32.whl", hash = "sha256:b4c037fe2f75bcd9aed0c89c7c507cb7fa59abae2bd4c8b6fc331a28178655a4"}, + {file = "ruff-0.0.270-py3-none-win_amd64.whl", hash = "sha256:0012f9b7dc137ab7f1f0355e3c4ca49b562baf6c9fa1180948deeb6648c52957"}, + {file = "ruff-0.0.270-py3-none-win_arm64.whl", hash = "sha256:9613456b0b375766244c25045e353bc8890c856431cd97893c97b10cc93bd28d"}, + {file = "ruff-0.0.270.tar.gz", hash = "sha256:95db07b7850b30ebf32b27fe98bc39e0ab99db3985edbbf0754d399eb2f0e690"}, ] [[package]] @@ -1720,21 +1197,6 @@ files = [ lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] -[[package]] -name = "stevedore" -version = "3.5.2" -description = "Manage dynamic plugins for Python applications" -optional = false -python-versions = ">=3.6" -files = [ - {file = "stevedore-3.5.2-py3-none-any.whl", hash = "sha256:fa2630e3d0ad3e22d4914aff2501445815b9a4467a6edc49387c667a38faf5bf"}, - {file = "stevedore-3.5.2.tar.gz", hash = "sha256:cf99f41fc0d5a4f185ca4d3d42b03be9011b0a1ec1a4ea1a282be1b4b306dcc2"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""} -pbr = ">=2.0.0,<2.1.0 || >2.1.0" - [[package]] name = "toml" version = "0.10.2" @@ -1803,6 +1265,17 @@ tox = ">=4,<5" [package.extras] testing = ["black", "devpi-process", "flake8 (>=6,<7)", "mypy", "pytest (>=7,<8)", "pytest-cov (>=3,<4)", "pytest-mock (>=3,<4)", "pytest-randomly (>=3)"] +[[package]] +name = "trove-classifiers" +version = "2023.5.24" +description = "Canonical source for classifiers on PyPI (pypi.org)." +optional = false +python-versions = "*" +files = [ + {file = "trove-classifiers-2023.5.24.tar.gz", hash = "sha256:fd5a1546283be941f47540a135bdeae8fb261380a6a204d9c18012f2a1b0ceae"}, + {file = "trove_classifiers-2023.5.24-py3-none-any.whl", hash = "sha256:d9d7ae14fb90bf3d50bef99c3941b176b5326509e6e9037e622562d6352629d0"}, +] + [[package]] name = "typed-ast" version = "1.5.4" @@ -1878,6 +1351,9 @@ files = [ [package.dependencies] fastjsonschema = ">=2.16.2,<=3" importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +packaging = {version = ">=20.4", optional = true, markers = "extra == \"all\""} +tomli = {version = ">=1.2.1", optional = true, markers = "python_version < \"3.11\" and extra == \"all\""} +trove-classifiers = {version = ">=2021.10.20", optional = true, markers = "extra == \"all\""} [package.extras] all = ["packaging (>=20.4)", "tomli (>=1.2.1)", "trove-classifiers (>=2021.10.20)"] @@ -1937,4 +1413,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "24fe265ef25bfdea3aef075cc90243b962de6c7c40011157f247e703baa955a8" +content-hash = "fee59ec718eacf6f18491eda80a087d7468950c45ce3d76121d24dcb2fc44981" diff --git a/pyproject.toml b/pyproject.toml index 5c7cb9d..0e4f783 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -188,36 +188,16 @@ pytest-benchmark = "*" pytest-cov = "*" pytest-xdist = "*" psutil = "*" +pygments = "^2.13" [tool.poetry.group.lint.dependencies] -check-manifest = "^0.49" -codespell = ">=2.2.2" +codespell = ">=2.2.4" tomli = { version = "*", python = "<3.12" } -flake8 = { version = "*", python = ">=3.8.1,<4.0" } -flake8-bugbear = "^23.1.20" -flake8-import-order = "^0.18.2" -flake8-tidy-imports = "^4.8.0" -flake8-print = "^5.0.0" -flake8-builtins = "^2.1.0" -flake8-blind-except = "^0.2.1" -flake8-comprehensions = "^3.10.1" -flake8-debugger = "^4.1.2" -pep8-naming = "^0.13.3" -flake8-string-format = "^0.3.0" -flake8-mutable = "^1.2.0" -flake8-pytest-style = "^1.6.0" -# TODO: flake8-annotations 3.0.0 removes support for PEP-484 type comments -flake8-annotations = "<3.0.0" -flake8-black = "*" -# TODO: replace flake8-mypy with proper execution of mypy -# NOTE: the released version of flake8-mypy is buggy on Windows (17.8.0) -flake8-mypy = { git = "https://github.com/ambv/flake8-mypy.git" } -bandit = "^1.6.0" -black = "^22.12.0" -vulture = "^2.7" -validate-pyproject = "*" -mypy = "*" - +ruff = "*" +vulture = "*" +validate-pyproject = {extras = ["all"], version = "^0.13"} +black = "^23.3.0" +mypy = "^1.3.0" [tool.poetry.group.docs.dependencies] furo = {version = "*", python = ">=3.8,<4.0"} @@ -253,10 +233,77 @@ check-filenames = true skip = '*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,*.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist' ignore-words-list = 'ether,Expresso,expresso,Espresso,espresso' -[tool.bandit] -recursive = true -targets = ["getmac"] -skips = ["B404", "B603", "B104"] -[tool.black] -target-version = ["py36", "py37", "py38", "py39", "py310", "py311"] +# https://beta.ruff.rs/docs +[tool.ruff] +line-length = 99 +# show-source = true + +# Rules: https://beta.ruff.rs/docs/rules +# If you violate a rule, lookup the rule on the Rules page in ruff docs. +# Many rules have links you can click with a explanation of the rule and how to fix it. +# If there isn't a link, go to the project the rule was source from (e.g. flake8-bugbear) +# and review it's docs for the corresponding rule. +# If you're still confused, ask a fellow developer for assistance. +# You can also run "ruff rule " to explain a rule on the command line, without a browser or internet access. +select = [ + "F", # Pyflakes + "E", # pycodestyle + "W", # Warning + "N", + # "ANN", # flake8-annotations # TODO: move to python3 type annotations + "S", # flake8-bandit + "B", # flake8-bugbear + "A", # flake8-builtins + "C4", # flake8-comprehensions + "DTZ", # flake8-datetimez + "T10", # flake8-debugger + "EXE", # flake8-executable, + "ISC", # flake8-implicit-str-concat + "G", # flake8-logging-format + "PIE", # flake8-pie + "T20", # flake8-print + "PT", # flake8-pytest-style + "RSE", # flake8-raise + "RET", # flake8-return + "TID", # flake8-tidy-imports + "ARG", # flake8-unused-arguments + "PGH", # pygrep-hooks + "PLC", # Pylint Convention + "PLE", # Pylint Errors + "PLW", # Pylint Warnings + "RUF", # Ruff-specific rules + # TODO: move to pathlib? + # "PTH", # flake8-use-pathlib +] + +# Files to exclude from linting +extend-exclude = [ + "*.pyc", + "__pycache__", + ".tox", + ".venv", + ".github", + ".vagrant", + "docs", + "packaging", + "samples", + "scripts", +] + +# ignore = E203, W503, E401, F401, E226, B902, ANN101, ANN102, T499 +# Linting error codes to ignore +ignore = [ + "ANN001", + "ANN102", + "RET505", # unnecessary 'else' after 'return' statement + "PLW0603", # Using the global statement to update is discouraged + "PLW2901", # for loop variable overwritten by assignment target + "G004", # Logging statement uses f-string + "PIE790", # no-unnecessary-pass + "PGH003", # Use specific rule codes when ignoring type issues + "PLC1901", # compare-to-empty-string +] + +[tool.ruff.per-file-ignores] +"tests/*.py" = ["ANN001", "ANN002", "ANN003", "ANN201", "ANN202", "S101", "S104", "S603"] diff --git a/tests/test_cli.py b/tests/test_cli.py index a1be3ac..7d42318 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,8 +2,6 @@ import sys from subprocess import PIPE, Popen -import pytest - from getmac import __version__, get_mac_address BASE_CMD = [sys.executable, "-m", "getmac"] @@ -20,52 +18,52 @@ def test_cli_main_basic(): def test_cli_main_verbose(): - assert get_mac_address() in run_cmd(BASE_CMD + ["--verbose"]) + assert get_mac_address() in run_cmd([*BASE_CMD, "--verbose"]) def test_cli_main_debug(): - assert get_mac_address() in run_cmd(BASE_CMD + ["--verbose", "--debug"]) + assert get_mac_address() in run_cmd([*BASE_CMD, "--verbose", "--debug"]) def test_cli_main_invalid_interface(): - assert run_cmd(BASE_CMD + ["--interface", "INVALIDTESTINTERFACE"]) == "" + assert run_cmd([*BASE_CMD, "--interface", "INVALIDTESTINTERFACE"]) == "" def test_cli_help(): - assert run_cmd(BASE_CMD + ["--help"]) != "" + assert run_cmd([*BASE_CMD, "--help"]) != "" def test_cli_version(): - assert __version__ in run_cmd(BASE_CMD + ["--version"]) + assert __version__ in run_cmd([*BASE_CMD, "--version"]) def test_cli_multiple_debug_levels(): - assert get_mac_address() in run_cmd(BASE_CMD + ["-v", "-dd"]) - assert get_mac_address() in run_cmd(BASE_CMD + ["-v", "-ddd"]) - assert get_mac_address() in run_cmd(BASE_CMD + ["-v", "-dddd"]) + assert get_mac_address() in run_cmd([*BASE_CMD, "-v", "-dd"]) + assert get_mac_address() in run_cmd([*BASE_CMD, "-v", "-ddd"]) + assert get_mac_address() in run_cmd([*BASE_CMD, "-v", "-dddd"]) def test_cli_no_net(): assert get_mac_address(hostname="localhost") in run_cmd( - BASE_CMD + ["-n", "localhost", "--no-network-requests"] + [*BASE_CMD, "-n", "localhost", "--no-network-requests"] ) def test_cli_override_port(): assert ( - run_cmd(BASE_CMD + ["-v", "-dd", "-4", "127.0.0.1", "--override-port", "44444"]) + run_cmd([*BASE_CMD, "-v", "-dd", "-4", "127.0.0.1", "--override-port", "44444"]) != "" ) def test_cli_localhost(): - assert run_cmd(BASE_CMD + ["-4", "127.0.0.1"]) != "" - assert run_cmd(BASE_CMD + ["-n", "localhost"]) != "" - assert run_cmd(BASE_CMD + ["--no-network-requests", "-4", "127.0.0.1"]) != "" - assert run_cmd(BASE_CMD + ["--no-network-requests", "-n", "localhost"]) != "" + assert run_cmd([*BASE_CMD, "-4", "127.0.0.1"]) != "" + assert run_cmd([*BASE_CMD, "-n", "localhost"]) != "" + assert run_cmd([*BASE_CMD, "--no-network-requests", "-4", "127.0.0.1"]) != "" + assert run_cmd([*BASE_CMD, "--no-network-requests", "-n", "localhost"]) != "" # TODO: figure out how to properly test CLI commands and isolate platform-specific behavior def test_cli_override_platform(): plat = platform.system().lower() - assert run_cmd(BASE_CMD + ["-v", "-dd", "--override-platform", plat]) != "" + assert run_cmd([*BASE_CMD, "-v", "-dd", "--override-platform", plat]) != "" diff --git a/tests/test_getmac.py b/tests/test_getmac.py index d63329c..2983051 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -1,5 +1,4 @@ import inspect -import sys from subprocess import CalledProcessError import pytest @@ -65,7 +64,7 @@ def test_call_proc(mocker): mocker.patch("getmac.getmac.WINDOWS", True) m = mocker.patch("getmac.getmac.check_output", return_value="WINSUCCESS") assert getmac._call_proc("CMD", "arg") == "WINSUCCESS" - m.assert_called_once_with("CMD" + " " + "arg", stderr="DEVNULL", env="ENV") + m.assert_called_once_with("CMD arg", stderr="DEVNULL", env="ENV") mocker.patch("getmac.getmac.WINDOWS", False) m = mocker.patch("getmac.getmac.check_output", return_value="YAY") diff --git a/tox.ini b/tox.ini index 0b0a750..c0efc37 100644 --- a/tox.ini +++ b/tox.ini @@ -35,8 +35,9 @@ platform = linux: linux macos: darwin windows: win32 -allowlist_externals = - poetry +allowlist_externals = poetry +commands_pre = + poetry install --no-root --sync ; TODO: --benchmark-enable? commands = poetry run pytest \ @@ -56,20 +57,9 @@ setenv = MYPYPATH=getmac basepython = py311, py310, py3 commands = - poetry run codespell - poetry run flake8 getmac tests - poetry run bandit -q -r -c {toxinidir}/pyproject.toml getmac + poetry run codespell --toml {toxinidir}/pyproject.toml + poetry run ruff check getmac tests poetry run vulture --min-confidence 100 getmac tests poetry run validate-pyproject {toxinidir}/pyproject.toml - poetry run check-manifest --quiet {toxinidir} - -[flake8] -max-line-length = 99 -application-import-names = getmac -exclude = .github, .vagrant, docs, packaging, samples, scripts, *.egg-info, __pycache__, .git, .tox, .venv -ignore = E203, W503, E401, F401, E226, B902, ANN101, ANN102, T499 -per-file-ignores = - tests/*.py:ANN001,ANN002,ANN003,ANN201,ANN202 -import-order-style = pycharm -show-source = True -max-complexity = 38 + poetry run black --check --quiet getmac tests + poetry run mypy getmac tests From d7370466fdf37794573dbdf560eafe2d89e2282d Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 00:41:35 -0600 Subject: [PATCH 018/112] tox cleanup --- tox.ini | 3 --- 1 file changed, 3 deletions(-) diff --git a/tox.ini b/tox.ini index c0efc37..bff9137 100644 --- a/tox.ini +++ b/tox.ini @@ -52,9 +52,6 @@ commands = [testenv:check] description = Run code quality, static analysis, and spelling checks -setenv = - PIP_DISABLE_PIP_VERSION_CHECK = 1 - MYPYPATH=getmac basepython = py311, py310, py3 commands = poetry run codespell --toml {toxinidir}/pyproject.toml From 24da811810761e31efbeaca0d1fcd2b88c5a5130 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 01:01:10 -0600 Subject: [PATCH 019/112] Type annotations! Wow! --- .editorconfig | 6 ++ docs/TODO.md | 12 +-- getmac/__main__.py | 2 +- getmac/getmac.py | 212 +++++++++++++++++++++------------------------ pyproject.toml | 11 +-- 5 files changed, 115 insertions(+), 128 deletions(-) diff --git a/.editorconfig b/.editorconfig index f6051be..7b3da08 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,6 +16,12 @@ trim_trailing_whitespace = true # Use 2 spaces for the HTML files [*.{yml,yaml}] indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.toml] +insert_final_newline = true +trim_trailing_whitespace = true # The JSON files contain newlines inconsistently [*.json] diff --git a/docs/TODO.md b/docs/TODO.md index c9c4452..5e74589 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -6,7 +6,6 @@ - get_mac_address() is caching an old mac address for a given IP, even when it has timeout from OS ARP table. Only an explicit delete of the ARP entry on the OS make it return '00:00:00:00:00:00' again. - Fix is to check that the flag != 0x0, which should do the trick, unless there's an edge case that it misses. - [x] Switch to Poetry for project management - - [ ] Also, add `isort` - [x] Support Python 3.10 and 3.11 - [x] Update pytest (pytest 4, which we were using to support python 2.7, doesn't work with python 3.10) - [x] add tests + setup.py classifier @@ -23,7 +22,6 @@ - [ ] **Consolidate `ip6` argument into `ip` argument.**. Parse based on `::` character vs `.` character if `str` or via `.version == 4`/`.version == 6` for `ipaddress` objects. - Combine `--ip` and `--ip6` CLI arguments into `--ip` - [ ] Support `ipaddress` objects, `IPv4Address` and `IPv6Address` -- [ ] Move method classes into a separate file - [ ] Add new method: `get_default_interface()`. This leverages the default interface detection methods to expose a helpful public API. - [ ] Add test to ensure only the expected files make it into the sdist and wheel, no unexpected files - [ ] Split utils into a separate file @@ -79,7 +77,7 @@ - [ ] finer-grained platform support identification for methods by versions/releases, e.g. Windows 7 vs 10, Ubuntu 12 vs 20 - [ ] CLI: put "override" and other debugging-related arguments into a separate argparse argument group - [ ] Refactor to build a local state of the interfaces on the system, and use that as fallback for default lookup of interface with no name. Could also include MACs for faster lookup of future interface queries. Similar to how `netifaces` works, with a dict with interface infos. Properly address https://github.com/GhostofGoes/getmac/issues/78 -- [ ] Replace `flake8-mypy` with proper execution of mypy in tests (the project is dead and archived, https://github.com/ambv/flake8-mypy) +- [x] Replace `flake8-mypy` with proper execution of mypy in tests (the project is dead and archived, https://github.com/ambv/flake8-mypy) - [ ] Add a deprecation warning to `get-mac` package, don't publish it for 1.0.0 - [ ] Add all project contributors to list of Authors in package metadata in `pyproject.toml` (and the documentation) - [ ] Update supported versions table in [SECURITY.md](../SECURITY.md) @@ -87,15 +85,10 @@ ## Py3-related stuff for 1.0.0 - [x] Drop support for python 2.7, 3.4, and 3.5 - [x] BUMP TEST DEPENDENCIES AND PYTEST VERSION TO MODERN TIMES (especially pytest...) -- [ ] cache package imports done during test for use during `get()`, reuse - [ ] rewrite strings to f-strings - [x] Use `pyproject.toml` instead of `setup.py` - - https://packaging.python.org/tutorials/packaging-projects/ - - Move configurations for tools out of `tox.ini` and into `pyproject.toml` - - Add codespell configuration, remove CLI arguments - - Add linting of pyproject.toml, remove checking of setup.py - [x] update classifiers in setup.py -- [ ] add inline type annotations for method arguments. remove types from docstrings? +- [x] add inline type annotations for method arguments. remove types from docstrings? - [x] Remove `shutilwhich.py` and `.coveragerc` @@ -103,6 +96,7 @@ - [ ] Refactor the default interface code. Combine the functions into one, move the default fallback logic into the function. - TODO: MAC -> IP. "to_find='mac'"? (create GitHub issue?) +- [ ] Move method classes into a separate file # Bugs or potential issues - [ ] Fix lookup of a IPv4 address of a local interface on Linux diff --git a/getmac/__main__.py b/getmac/__main__.py index 0e5b1b8..6b2c09b 100644 --- a/getmac/__main__.py +++ b/getmac/__main__.py @@ -7,7 +7,7 @@ from . import getmac -def main(): # type: () -> None +def main() -> None: parser = argparse.ArgumentParser( "getmac", description="Get the MAC address of system network " diff --git a/getmac/getmac.py b/getmac/getmac.py index 31782b6..978a6fb 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -135,20 +135,17 @@ CHECK_COMMAND_CACHE = {} # type: Dict[str, bool] -def check_command(command): - # type: (str) -> bool +def check_command(command: str) -> bool: if command not in CHECK_COMMAND_CACHE: CHECK_COMMAND_CACHE[command] = bool(which(command, path=PATH_STR)) return CHECK_COMMAND_CACHE[command] -def check_path(filepath): - # type: (str) -> bool +def check_path(filepath: str) -> bool: return os.path.exists(filepath) and os.access(filepath, os.R_OK) -def _clean_mac(mac): - # type: (Optional[str]) -> Optional[str] +def _clean_mac(mac: Optional[str]) -> Optional[str]: """Check and format a string result to be lowercase colon-separated MAC.""" if mac is None: return None @@ -196,8 +193,7 @@ def _clean_mac(mac): return mac -def _read_file(filepath): - # type: (str) -> Optional[str] +def _read_file(filepath: str) -> Optional[str]: try: with open(filepath) as f: return f.read() @@ -206,8 +202,9 @@ def _read_file(filepath): return None -def _search(regex, text, group_index=0, flags=0): - # type: (str, str, int, int) -> Optional[str] +def _search( + regex: str, text: str, group_index: int = 0, flags: int = 0 +) -> Optional[str]: if not text: if DEBUG: log.debug("No text to _search()") @@ -220,8 +217,7 @@ def _search(regex, text, group_index=0, flags=0): return None -def _popen(command, args): - # type: (str, str) -> str +def _popen(command: str, args: str) -> str: for directory in PATH: executable = os.path.join(directory, command) if ( @@ -239,8 +235,7 @@ def _popen(command, args): return _call_proc(executable, args) -def _call_proc(executable, args): - # type: (str, str) -> str +def _call_proc(executable: str, args: str) -> str: if WINDOWS: cmd = executable + " " + args # type: ignore else: @@ -259,13 +254,11 @@ def _call_proc(executable, args): return output -def _uuid_convert(mac): - # type: (int) -> str +def _uuid_convert(mac: int) -> str: return ":".join(("%012X" % mac)[i : i + 2] for i in range(0, 12, 2)) -def _fetch_ip_using_dns(): - # type: () -> str +def _fetch_ip_using_dns() -> str: """ Determines the IP address of the default network interface. @@ -293,25 +286,27 @@ class Method: } #: Platforms supported by a method - platforms = set() # type: Set[str] + platforms: Set[str] = set() #: The type of method, e.g. does it get the MAC of a interface? #: Allowed values: {ip, ip4, ip6, iface, default_iface} - method_type = "" # type: str + method_type: str = "" #: If the method makes a network request as part of the check - network_request = False # type: bool + network_request: bool = False #: Marks the method as unable to be used, e.g. if there was a runtime #: error indicating the method won't work on the current platform. - unusable = False # type: bool + unusable: bool = False - def test(self): # type: () -> bool - """Low-impact test that the method is feasible, e.g. command exists.""" + def test(self) -> bool: + """ + Low-impact test that the method is feasible, e.g. command exists. + """ return False # pragma: no cover # TODO: automatically clean MAC on return - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: # noqa: ARG002 """ Core logic of the method that performs the lookup. @@ -329,10 +324,10 @@ def get(self, arg): # type: (str) -> Optional[str] Lowercase colon-separated MAC address, or None if one could not be found. """ - pass # pragma: no cover + return None # pragma: no cover @classmethod - def __str__(cls): # type: () -> str + def __str__(cls) -> str: return cls.__name__ @@ -342,7 +337,7 @@ class UuidArpGetNode(Method): platforms = {"linux", "darwin", "sunos", "other"} method_type = "ip" - def test(self): # type: () -> bool + def test(self) -> bool: try: from uuid import _arp_getnode # type: ignore # noqa: F401 @@ -350,7 +345,7 @@ def test(self): # type: () -> bool except Exception: return False - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: from uuid import _arp_getnode # type: ignore backup = socket.gethostbyname @@ -376,10 +371,10 @@ class ArpFile(Method): method_type = "ip4" _path = os.environ.get("ARP_PATH", "/proc/net/arp") # type: str - def test(self): # type: () -> bool + def test(self) -> bool: return check_path(self._path) - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: if not arg: return None @@ -401,10 +396,10 @@ class ArpFreebsd(Method): platforms = {"freebsd"} method_type = "ip" - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("arp") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: regex = r"\(" + re.escape(arg) + r"\)\s+at\s+" + MAC_RE_COLON return _search(regex, _popen("arp", arg)) @@ -414,10 +409,10 @@ class ArpOpenbsd(Method): method_type = "ip" _regex = r"[ ]+" + MAC_RE_COLON # type: str - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("arp") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: return _search(re.escape(arg) + self._regex, _popen("arp", "-an")) @@ -439,10 +434,10 @@ class ArpVariousArgs(Method): _good_pair = () # type: Union[Tuple, Tuple[str, bool]] _good_regex = "" # type: str - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("arp") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: if not arg: return None @@ -516,13 +511,13 @@ class ArpExe(Method): platforms = {"windows", "wsl"} method_type = "ip4" - def test(self): # type: () -> bool + def test(self) -> bool: # NOTE: specifying "arp.exe" instead of "arp" lets this work # seamlessly on WSL1 as well. On WSL2 it doesn't matter, since # it's basically just a Linux VM with some lipstick. return check_command("arp.exe") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: return _search(MAC_RE_DASH, _popen("arp.exe", "-a %s" % arg)) @@ -560,10 +555,10 @@ class ArpingHost(Method): _habets_args = "-r -C 1 -c 1 %s" # type: str _iputils_args = "-f -c 1 %s" # type: str - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("arping") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: # If busybox or iputils, this will just work, and if host ping fails, # then it'll exit with code 1 and this function will return None. # @@ -597,7 +592,7 @@ def get(self, arg): # type: (str) -> Optional[str] return None - def _call_habets(self, arg): # type: (str) -> Optional[str] + def _call_habets(self, arg: str) -> Optional[str]: command_output = _popen("arping", self._habets_args % arg) if command_output: return command_output.strip() @@ -617,13 +612,13 @@ class CtypesHost(Method): method_type = "ip4" network_request = True - def test(self): # type: () -> bool + def test(self) -> bool: try: return ctypes.windll.wsock32.inet_addr(b"127.0.0.1") > 0 except Exception: return False - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: # Convert to bytes on Python 3+ (Fixes GitHub issue #7) arg = arg.encode() # type: ignore @@ -662,10 +657,10 @@ class IpNeighborShow(Method): platforms = {"linux", "other"} method_type = "ip" # IPv6 and IPv4 - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("ip") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: output = _popen("ip", "neighbor show %s" % arg) if not output: return None @@ -685,11 +680,11 @@ class SysIfaceFile(Method): method_type = "iface" _path = "/sys/class/net/" # type: str - def test(self): # type: () -> bool + def test(self) -> bool: # Imperfect, but should work well enough return check_path(self._path) - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: data = _read_file(self._path + arg + "/address") # NOTE: if "/sys/class/net/" exists, but interface file doesn't, @@ -702,7 +697,7 @@ class UuidLanscan(Method): platforms = {"other"} method_type = "iface" - def test(self): # type: () -> bool + def test(self) -> bool: try: from uuid import _find_mac # type: ignore # noqa: F401 @@ -710,7 +705,7 @@ def test(self): # type: () -> bool except Exception: return False - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: from uuid import _find_mac # type: ignore arg = bytes(arg, "utf-8") # type: ignore @@ -727,7 +722,7 @@ class FcntlIface(Method): platforms = {"linux", "wsl"} method_type = "iface" - def test(self): # type: () -> bool + def test(self) -> bool: try: import fcntl # noqa: F401 @@ -735,7 +730,7 @@ def test(self): # type: () -> bool except Exception: # Broad except to handle unknown effects return False - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: import fcntl arg = arg.encode() # type: ignore @@ -765,13 +760,13 @@ class GetmacExe(Method): ] # type: List[Tuple[str, str]] _champ = () # type: Union[tuple, Tuple[str, str]] - def test(self): # type: () -> bool + def test(self) -> bool: # NOTE: the scripts from this library (getmac) are excluded from the # path used for checking variables, in getmac.getmac.PATH (defined # at the top of this file). Otherwise, this would get messy quickly :) return check_command("getmac.exe") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: try: # /nh: Suppresses table headers # /v: Verbose @@ -810,10 +805,10 @@ class IpconfigExe(Method): r"(?:\n?[^\n]*){1,8}Physical Address[ .:]+" + MAC_RE_DASH + r"\r\n" ) # type: str - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("ipconfig.exe") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: return _search(arg + self._regex, _popen("ipconfig.exe", "/all")) @@ -832,10 +827,10 @@ class WmicExe(Method): platforms = {"windows"} method_type = "iface" - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("wmic.exe") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: command_output = _popen( "wmic.exe", 'nic where "NetConnectionID = \'%s\'" get "MACAddress" /value' % arg, @@ -865,10 +860,10 @@ class DarwinNetworksetupIface(Method): platforms = {"darwin"} method_type = "iface" - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("networksetup") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: command_output = _popen("networksetup", "-getmacaddress %s" % arg) return _search(MAC_RE_COLON, command_output) @@ -886,8 +881,7 @@ def get(self, arg): # type: (str) -> Optional[str] ) -def _parse_ifconfig(iface, command_output): - # type: (str, str) -> Optional[str] +def _parse_ifconfig(iface: str, command_output: str) -> Optional[str]: if not iface or not command_output: return None @@ -909,10 +903,10 @@ class IfconfigWithIfaceArg(Method): platforms = {"linux", "wsl", "freebsd", "openbsd", "other"} method_type = "iface" - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("ifconfig") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: try: command_output = _popen("ifconfig", arg) except CalledProcessError as err: @@ -933,10 +927,10 @@ class IfconfigEther(Method): _tested_arg = False # type: bool _iface_arg = False # type: bool - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("ifconfig") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: # Check if this version of "ifconfig" accepts an interface argument command_output = "" @@ -975,10 +969,10 @@ class IfconfigOther(Method): _args_tested = False # type: bool _good_pair = [] # type: List[Union[str, Tuple[str, str]]] - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("ifconfig") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: if not arg: return None @@ -1034,19 +1028,19 @@ class NetstatIface(Method): # ".*?": non-greedy # https://docs.python.org/3/howto/regex.html#greedy-versus-non-greedy - _regexes = [ + _regexes: List[str] = [ r": .*?ether " + MAC_RE_COLON, r": .*?HWaddr " + MAC_RE_COLON, # Ubuntu 12.04 and other older kernels r" .*?Link encap:Ethernet HWaddr " + MAC_RE_COLON, - ] # type: List[str] - _working_regex = "" # type: str + ] + _working_regex: str = "" - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("netstat") # TODO: consolidate the parsing logic between IfconfigOther and netstat - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: # NOTE: netstat and ifconfig pull from the same kernel source and # therefore have the same output format on the same platform. command_output = _popen("netstat", "-iae") @@ -1091,10 +1085,10 @@ class IpLinkIface(Method): _tested_arg = False # type: bool _iface_arg = False # type: bool - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("ip") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: # Check if this version of "ip link" accepts an interface argument # Not accepting one is a quirk of older versions of 'iproute2' # TODO: is it "ip link " on some platforms and "ip link show " on others? @@ -1133,10 +1127,10 @@ class DefaultIfaceLinuxRouteFile(Method): platforms = {"linux", "wsl"} method_type = "default_iface" - def test(self): # type: () -> bool + def test(self) -> bool: return check_path("/proc/net/route") - def get(self, arg=""): # type: (str) -> Optional[str] # noqa: ARG002 + def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 data = _read_file("/proc/net/route") if data is not None and len(data) > 1: @@ -1171,10 +1165,10 @@ class DefaultIfaceRouteCommand(Method): platforms = {"linux", "wsl", "other"} method_type = "default_iface" - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("route") - def get(self, arg=""): # type: (str) -> Optional[str] + def get(self, arg: str = "") -> Optional[str]: output = _popen("route", "-n") try: @@ -1192,10 +1186,10 @@ class DefaultIfaceRouteGetCommand(Method): platforms = {"darwin", "freebsd", "other"} method_type = "default_iface" - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("route") - def get(self, arg=""): # type: (str) -> Optional[str] + def get(self, arg: str = "") -> Optional[str]: output = _popen("route", "get default") if not output: @@ -1214,10 +1208,10 @@ class DefaultIfaceIpRoute(Method): platforms = {"linux", "wsl", "other"} method_type = "default_iface" - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("ip") - def get(self, arg=""): # type: (str) -> Optional[str] # noqa: ARG002 + def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 output = _popen("ip", "route list 0/0") if not output: @@ -1232,10 +1226,10 @@ class DefaultIfaceOpenBsd(Method): platforms = {"openbsd"} method_type = "default_iface" - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("route") - def get(self, arg=""): # type: (str) -> Optional[str] # noqa: ARG002 + def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 output = _popen("route", "-nq show -inet -gateway -priority 1") return output.partition("127.0.0.1")[0].strip().rpartition(" ")[2] @@ -1244,10 +1238,10 @@ class DefaultIfaceFreeBsd(Method): platforms = {"freebsd"} method_type = "default_iface" - def test(self): # type: () -> bool + def test(self) -> bool: return check_command("netstat") - def get(self, arg=""): # type: (str) -> Optional[str] # noqa: ARG002 + def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 output = _popen("netstat", "-r") return _search(r"default[ ]+\S+[ ]+\S+[ ]+(\S+)[\r\n]+", output) @@ -1286,31 +1280,30 @@ def get(self, arg=""): # type: (str) -> Optional[str] # noqa: ARG002 ] # type: List[Type[Method]] # Primary method to use for a given method type -METHOD_CACHE = { +METHOD_CACHE: Dict[str, Optional[Method]] = { "ip4": None, "ip6": None, "iface": None, "default_iface": None, -} # type: Dict[str, Optional[Method]] +} # Order of methods is determined by: # Platform + version # Performance (file read > command) # Reliability (how well I know/understand the command to work) -FALLBACK_CACHE = { +FALLBACK_CACHE: Dict[str, List[Method]] = { "ip4": [], "ip6": [], "iface": [], "default_iface": [], -} # type: Dict[str, List[Method]] +} DEFAULT_IFACE = "" # type: str -def get_method_by_name(method_name): - # type: (str) -> Optional[Type[Method]] +def get_method_by_name(method_name: str) -> Optional[Type[Method]]: for method in METHODS: if method.__name__.lower() == method_name.lower(): return method @@ -1318,8 +1311,7 @@ def get_method_by_name(method_name): return None -def get_instance_from_cache(method_type, method_name): - # type: (str, str) -> Optional[Method] +def get_instance_from_cache(method_type: str, method_name: str) -> Optional[Method]: """ Get the class for a named Method from the caches. @@ -1342,8 +1334,7 @@ def get_instance_from_cache(method_type, method_name): return None -def _swap_method_fallback(method_type, swap_with): - # type: (str, str) -> bool +def _swap_method_fallback(method_type: str, swap_with: str) -> bool: if str(METHOD_CACHE[method_type]) == swap_with: return True @@ -1364,8 +1355,7 @@ def _swap_method_fallback(method_type, swap_with): return True -def _warn_critical(err_msg): - # type: (str) -> None +def _warn_critical(err_msg: str) -> None: log.critical(err_msg) warnings.warn( # noqa: B028 "%s. NOTICE: this warning will likely turn into a raised exception in getmac 1.0.0!" @@ -1374,9 +1364,7 @@ def _warn_critical(err_msg): ) -def initialize_method_cache( - method_type, network_request=True -): # type: (str, bool) -> bool +def initialize_method_cache(method_type: str, network_request: bool = True) -> bool: """ Initialize the method cache for the given method type. @@ -1526,7 +1514,7 @@ def initialize_method_cache( return True -def _remove_unusable(method, method_type): # type: (Method, str) -> Optional[Method] +def _remove_unusable(method: Method, method_type: str) -> Optional[Method]: if not FALLBACK_CACHE[method_type]: log.warning("No fallback method for unusable method '%s'!", str(method)) METHOD_CACHE[method_type] = None @@ -1541,9 +1529,7 @@ def _remove_unusable(method, method_type): # type: (Method, str) -> Optional[Me return METHOD_CACHE[method_type] -def _attempt_method_get( - method, method_type, arg -): # type: (Method, str, str) -> Optional[str] +def _attempt_method_get(method: Method, method_type: str, arg: str) -> Optional[str]: """ Attempt to use methods, and if they fail, fallback to the next method in the cache. """ @@ -1603,8 +1589,9 @@ def _attempt_method_get( return result -def get_by_method(method_type, arg="", network_request=True): - # type: (str, str, bool) -> Optional[str] +def get_by_method( + method_type: str, arg: str = "", network_request: bool = True +) -> Optional[str]: """ Query for a MAC using a specific method. @@ -1670,9 +1657,12 @@ def get_by_method(method_type, arg="", network_request=True): def get_mac_address( - interface=None, ip=None, ip6=None, hostname=None, network_request=True -): - # type: (Optional[str], Optional[str], Optional[str], Optional[str], bool) -> Optional[str] + interface: Optional[str] = None, + ip: Optional[str] = None, + ip6: Optional[str] = None, + hostname: Optional[str] = None, + network_request: bool = True, +) -> Optional[str]: """ Get an Unicast IEEE 802 MAC-48 address from a local interface or remote host. diff --git a/pyproject.toml b/pyproject.toml index 0e4f783..fdf8101 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -250,8 +250,8 @@ select = [ "F", # Pyflakes "E", # pycodestyle "W", # Warning - "N", - # "ANN", # flake8-annotations # TODO: move to python3 type annotations + "N", # pep8-naming + "ANN", # flake8-annotations "S", # flake8-bandit "B", # flake8-bugbear "A", # flake8-builtins @@ -273,8 +273,6 @@ select = [ "PLE", # Pylint Errors "PLW", # Pylint Warnings "RUF", # Ruff-specific rules - # TODO: move to pathlib? - # "PTH", # flake8-use-pathlib ] # Files to exclude from linting @@ -291,11 +289,10 @@ extend-exclude = [ "scripts", ] -# ignore = E203, W503, E401, F401, E226, B902, ANN101, ANN102, T499 # Linting error codes to ignore ignore = [ - "ANN001", - "ANN102", + "ANN101", # missing-type-self + "ANN102", # missing-type-cls "RET505", # unnecessary 'else' after 'return' statement "PLW0603", # Using the global statement to update is discouraged "PLW2901", # for loop variable overwritten by assignment target From 8c04320906afcd504360d22539bab3bea88a937d Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 20:44:40 -0600 Subject: [PATCH 020/112] attempt to fix coveralls --- .coveragerc | 5 +++ poetry.lock | 104 ++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 3 files changed, 58 insertions(+), 53 deletions(-) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..18390c7 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +# TODO: this works around a issue in coveralls-python-action +# Once the fix is merged (it's been 2 years, I don't have high hopes), then this file can be removed +# https://github.com/AndreMiras/coveralls-python-action/pull/16 +[run] +relative_files = True \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 69a6acb..5443f74 100644 --- a/poetry.lock +++ b/poetry.lock @@ -529,61 +529,61 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "markupsafe" -version = "2.1.2" +version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, - {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] [[package]] @@ -1413,4 +1413,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "fee59ec718eacf6f18491eda80a087d7468950c45ce3d76121d24dcb2fc44981" +content-hash = "03eb8d5a9989b24d60c5296ac9f4f64fdcbd917ded998ae0d74dcc6ef73e81ef" diff --git a/pyproject.toml b/pyproject.toml index fdf8101..ed290c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -226,7 +226,7 @@ norecursedirs = ".github getmac samples scripts .git .tox .vagrant .idea .vscode addopts = "--tb=short" [tool.coverage.run] -relative_files = true +relative_files = true # required for coveralls [tool.codespell] check-filenames = true From 30de2e2570fe2892c05f96ab0efd5903f30eb8a3 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 20:50:01 -0600 Subject: [PATCH 021/112] attempt to fix poetry install on windows --- .github/workflows/ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77ca46b..f09882a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,11 @@ jobs: # See: https://github.com/actions/setup-python/issues/532 env: PIP_DISABLE_PIP_VERSION_CHECK: 1 + # Required for snok/install-poetry + # https://github.com/snok/install-poetry/tree/v1/#running-on-windows + defaults: + run: + shell: bash strategy: fail-fast: false # Versions available: @@ -93,7 +98,7 @@ jobs: run: poetry run tox - uses: codecov/codecov-action@v3 with: - # codecov is annoying. Not sure why I'm still using it, + # codecov is annoying. Not sure why I'm still using it, # may need to look at something that has less heisenbugs. # https://community.codecov.com/t/ci-failure-due-to-too-many-uploads-to-this-commit/2587/12 fail_ci_if_error: false From 2d8ca58f153ff0caeae7a106fb956516ac7b0c21 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 20:54:33 -0600 Subject: [PATCH 022/112] update lock --- poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 5443f74..82200b9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1413,4 +1413,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "03eb8d5a9989b24d60c5296ac9f4f64fdcbd917ded998ae0d74dcc6ef73e81ef" +content-hash = "fee59ec718eacf6f18491eda80a087d7468950c45ce3d76121d24dcb2fc44981" From 231ada95696b947cfc0e592604a946225e190ad8 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 21:19:23 -0600 Subject: [PATCH 023/112] Fix issues, type annotations --- getmac/getmac.py | 157 +++++++++++++++++++++-------------------------- pyproject.toml | 1 - 2 files changed, 71 insertions(+), 87 deletions(-) diff --git a/getmac/getmac.py b/getmac/getmac.py index 978a6fb..0cc7cb2 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -33,17 +33,8 @@ import traceback import warnings from shutil import which -from subprocess import CalledProcessError, DEVNULL, check_output - -# Used for mypy (a data type analysis tool) -# If you're copying the code, this section can be safely removed -try: - from typing import TYPE_CHECKING - - if TYPE_CHECKING: - from typing import Dict, List, Optional, Set, Tuple, Type, Union # noqa: F401 -except ImportError: - pass +from subprocess import DEVNULL, CalledProcessError, check_output +from typing import Dict, List, Optional, Set, Tuple, Type, Union # Configure logging log = logging.getLogger("getmac") # type: logging.Logger @@ -53,27 +44,27 @@ __version__ = "1.0.0a0" # Configurable settings -DEBUG = 0 # type: int -PORT = 55555 # type: int +DEBUG: int = 0 +PORT: int = 55555 # Platform identifiers -_UNAME = platform.uname() # type: platform.uname_result -_SYST = _UNAME.system # type: str +_UNAME: platform.uname_result = platform.uname() +_SYST: str = _UNAME.system -WINDOWS = _SYST == "Windows" # type: bool -DARWIN = _SYST == "Darwin" # type: bool +WINDOWS: bool = _SYST == "Windows" +DARWIN: bool = _SYST == "Darwin" -OPENBSD = _SYST == "OpenBSD" # type: bool -FREEBSD = _SYST == "FreeBSD" # type: bool -NETBSD = _SYST == "NetBSD" # type: bool -SOLARIS = _SYST == "SunOS" # type: bool +OPENBSD: bool = _SYST == "OpenBSD" +FREEBSD: bool = _SYST == "FreeBSD" +NETBSD: bool = _SYST == "NetBSD" +SOLARIS: bool = _SYST == "SunOS" # Not including Darwin or Solaris as a "BSD" -BSD = OPENBSD or FREEBSD or NETBSD # type: bool +BSD: bool = OPENBSD or FREEBSD or NETBSD # Windows Subsystem for Linux (WSL) -WSL = False # type: bool -LINUX = False # type: bool +WSL: bool = False +LINUX: bool = False if _SYST == "Linux": if "Microsoft" in platform.version(): WSL = True @@ -83,26 +74,24 @@ # NOTE: "Linux" methods apply to Android without modifications # If there's Android-specific stuff then we can add a platform # identifier for it. -ANDROID = ( - hasattr(sys, "getandroidapilevel") or "ANDROID_STORAGE" in os.environ -) # type: bool +ANDROID: bool = hasattr(sys, "getandroidapilevel") or "ANDROID_STORAGE" in os.environ # Generic platform identifier used for filtering methods -PLATFORM = _SYST.lower() # type: str +PLATFORM: str = _SYST.lower() if PLATFORM == "linux" and "Microsoft" in platform.version(): PLATFORM = "wsl" # User-configurable override to force a specific platform # This will change to a function argument in 1.0.0 -OVERRIDE_PLATFORM = "" # type: str +OVERRIDE_PLATFORM: str = "" # Force a specific method to be used for all lookups # Used for debugging and testing -FORCE_METHOD = "" # type: str +FORCE_METHOD: str = "" # Get and cache the configured system PATH on import # The process environment does not change after a process is started -PATH = os.environ.get("PATH", os.defpath).split(os.pathsep) # type: List[str] +PATH: List[str] = os.environ.get("PATH", os.defpath).split(os.pathsep) if not WINDOWS: PATH.extend(("/sbin", "/usr/sbin")) else: @@ -111,28 +100,28 @@ # This just handles case where it's in a virtualenv, won't work /w global scripts PATH = [p for p in PATH if "\\getmac\\Scripts" not in p] # Build the str after modifications are made -PATH_STR = os.pathsep.join(PATH) # type: str +PATH_STR: str = os.pathsep.join(PATH) # Use a copy of the environment so we don't # modify the process's current environment. -ENV = dict(os.environ) # type: Dict[str, str] +ENV: Dict[str, str] = dict(os.environ) ENV["LC_ALL"] = "C" # Ensure ASCII output so we parse correctly # Constants -IP4 = 0 -IP6 = 1 -INTERFACE = 2 -HOSTNAME = 3 +IP4: int = 0 +IP6: int = 1 +INTERFACE: int = 2 +HOSTNAME: int = 3 -MAC_RE_COLON = r"([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})" -MAC_RE_DASH = r"([0-9a-fA-F]{2}(?:-[0-9a-fA-F]{2}){5})" +MAC_RE_COLON: str = r"([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})" +MAC_RE_DASH: str = r"([0-9a-fA-F]{2}(?:-[0-9a-fA-F]{2}){5})" # On OSX, some MACs in arp output may have a single digit instead of two # Examples: "18:4f:32:5a:64:5", "14:cc:20:1a:99:0" # This can also happen on other platforms, like Solaris -MAC_RE_SHORT = r"([0-9a-fA-F]{1,2}(?::[0-9a-fA-F]{1,2}){5})" +MAC_RE_SHORT: str = r"([0-9a-fA-F]{1,2}(?::[0-9a-fA-F]{1,2}){5})" # Cache of commands that have been checked for existence by check_command() -CHECK_COMMAND_CACHE = {} # type: Dict[str, bool] +CHECK_COMMAND_CACHE: Dict[str, bool] = {} def check_command(command: str) -> bool: @@ -241,9 +230,7 @@ def _call_proc(executable: str, args: str) -> str: else: cmd = [executable, *shlex.split(args)] # type: ignore - output = check_output( - cmd, stderr=DEVNULL, env=ENV # noqa: S603 - ) # type: Union[bytes, str] + output: Union[str, bytes] = check_output(cmd, stderr=DEVNULL, env=ENV) # noqa: S603 if DEBUG >= 4: log.debug("Output from '%s' command: %s", executable, str(output)) @@ -273,7 +260,7 @@ def _fetch_ip_using_dns() -> str: class Method: #: Valid platform identifier strings - VALID_PLATFORM_NAMES = { + VALID_PLATFORM_NAMES: Set[str] = { "android", "darwin", "linux", @@ -369,7 +356,7 @@ def get(self, arg: str) -> Optional[str]: class ArpFile(Method): platforms = {"linux"} method_type = "ip4" - _path = os.environ.get("ARP_PATH", "/proc/net/arp") # type: str + _path: str = os.environ.get("ARP_PATH", "/proc/net/arp") def test(self) -> bool: return check_path(self._path) @@ -407,7 +394,7 @@ def get(self, arg: str) -> Optional[str]: class ArpOpenbsd(Method): platforms = {"openbsd"} method_type = "ip" - _regex = r"[ ]+" + MAC_RE_COLON # type: str + _regex: str = r"[ ]+" + MAC_RE_COLON def test(self) -> bool: return check_command("arp") @@ -419,8 +406,8 @@ def get(self, arg: str) -> Optional[str]: class ArpVariousArgs(Method): platforms = {"linux", "darwin", "freebsd", "sunos", "other"} method_type = "ip" - _regex_std = r"\)\s+at\s+" + MAC_RE_COLON # type: str - _regex_darwin = r"\)\s+at\s+" + MAC_RE_SHORT # type: str + _regex_std: str = r"\)\s+at\s+" + MAC_RE_COLON + _regex_darwin: str = r"\)\s+at\s+" + MAC_RE_SHORT _args = ( ("", True), # "arp 192.168.1.1" # Linux @@ -430,9 +417,9 @@ class ArpVariousArgs(Method): ("-a", False), # "arp -a" ("-a", True), # "arp -a 192.168.1.1" ) - _args_tested = False # type: bool - _good_pair = () # type: Union[Tuple, Tuple[str, bool]] - _good_regex = "" # type: str + _args_tested: bool = False + _good_pair: Union[Tuple, Tuple[str, bool]] = () + _good_regex: str = "" def test(self) -> bool: return check_command("arp") @@ -551,9 +538,9 @@ class ArpingHost(Method): platforms = {"linux", "darwin"} method_type = "ip4" network_request = True - _is_iputils = True # type: bool - _habets_args = "-r -C 1 -c 1 %s" # type: str - _iputils_args = "-f -c 1 %s" # type: str + _is_iputils: bool = True + _habets_args: str = "-r -C 1 -c 1 %s" + _iputils_args: str = "-f -c 1 %s" def test(self) -> bool: return check_command("arping") @@ -620,10 +607,10 @@ def test(self) -> bool: def get(self, arg: str) -> Optional[str]: # Convert to bytes on Python 3+ (Fixes GitHub issue #7) - arg = arg.encode() # type: ignore + encoded_arg = arg.encode() try: - inetaddr = ctypes.windll.wsock32.inet_addr(arg) # type: ignore + inetaddr = ctypes.windll.wsock32.inet_addr(encoded_arg) if inetaddr in (0, -1): raise Exception except Exception: @@ -631,19 +618,19 @@ def get(self, arg: str) -> Optional[str]: # We should be explicit about only accepting ipv4 addresses # and handle any hostname resolution in calling code hostip = socket.gethostbyname(arg) - inetaddr = ctypes.windll.wsock32.inet_addr(hostip) # type: ignore + inetaddr = ctypes.windll.wsock32.inet_addr(hostip) buffer = ctypes.c_buffer(6) addlen = ctypes.c_ulong(ctypes.sizeof(buffer)) # https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-sendarp - send_arp = ctypes.windll.Iphlpapi.SendARP # type: ignore + send_arp = ctypes.windll.Iphlpapi.SendARP if send_arp(inetaddr, 0, ctypes.byref(buffer), ctypes.byref(addlen)) != 0: return None # Convert binary data into a string. macaddr = "" - for intval in struct.unpack("BBBBBB", buffer): # type: ignore + for intval in struct.unpack("BBBBBB", buffer): if intval > 15: replacestr = "0x" else: @@ -678,7 +665,7 @@ def get(self, arg: str) -> Optional[str]: class SysIfaceFile(Method): platforms = {"linux", "wsl"} method_type = "iface" - _path = "/sys/class/net/" # type: str + _path: str = "/sys/class/net/" def test(self) -> bool: # Imperfect, but should work well enough @@ -708,9 +695,7 @@ def test(self) -> bool: def get(self, arg: str) -> Optional[str]: from uuid import _find_mac # type: ignore - arg = bytes(arg, "utf-8") # type: ignore - - mac = _find_mac("lanscan", "-ai", [arg], lambda i: 0) # noqa: ARG005 + mac = _find_mac("lanscan", "-ai", [arg.encode()], lambda i: 0) # noqa: ARG005 if mac: return _uuid_convert(mac) @@ -733,12 +718,14 @@ def test(self) -> bool: def get(self, arg: str) -> Optional[str]: import fcntl - arg = arg.encode() # type: ignore + encoded_arg = arg.encode() s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 0x8927 = SIOCGIFADDR - info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack("256s", arg[:15])) # type: ignore + info = fcntl.ioctl( # type: ignore + s.fileno(), 0x8927, struct.pack("256s", encoded_arg[:15]) + ) return ":".join(["%02x" % ord(chr(char)) for char in info[18:24]]) @@ -752,13 +739,13 @@ class GetmacExe(Method): platforms = {"windows"} method_type = "iface" - _regexes = [ + _regexes: List[Tuple[str, str]] = [ # Connection Name (r"\r\n", r".*" + MAC_RE_DASH + r".*\r\n"), # Network Adapter (the human-readable name) (r"\r\n.*", r".*" + MAC_RE_DASH + r".*\r\n"), - ] # type: List[Tuple[str, str]] - _champ = () # type: Union[tuple, Tuple[str, str]] + ] + _champ: Union[tuple, Tuple[str, str]] = () def test(self) -> bool: # NOTE: the scripts from this library (getmac) are excluded from the @@ -801,9 +788,7 @@ class IpconfigExe(Method): platforms = {"windows"} method_type = "iface" - _regex = ( - r"(?:\n?[^\n]*){1,8}Physical Address[ .:]+" + MAC_RE_DASH + r"\r\n" - ) # type: str + _regex: str = r"(?:\n?[^\n]*){1,8}Physical Address[ .:]+" + MAC_RE_DASH + r"\r\n" def test(self) -> bool: return check_command("ipconfig.exe") @@ -924,8 +909,8 @@ def get(self, arg: str) -> Optional[str]: class IfconfigEther(Method): platforms = {"darwin"} method_type = "iface" - _tested_arg = False # type: bool - _iface_arg = False # type: bool + _tested_arg: bool = False + _iface_arg: bool = False def test(self) -> bool: return check_command("ifconfig") @@ -966,8 +951,8 @@ class IfconfigOther(Method): ("-v", r".*?HWaddr\s"), ("-av", r".*?Ether\s"), ) - _args_tested = False # type: bool - _good_pair = [] # type: List[Union[str, Tuple[str, str]]] + _args_tested: bool = False + _good_pair: List[Union[str, Tuple[str, str]]] = [] def test(self) -> bool: return check_command("ifconfig") @@ -1081,9 +1066,9 @@ def get(self, arg: str) -> Optional[str]: class IpLinkIface(Method): platforms = {"linux", "wsl", "android", "other"} method_type = "iface" - _regex = r".*\n.*link/ether " + MAC_RE_COLON # type: str - _tested_arg = False # type: bool - _iface_arg = False # type: bool + _regex: str = r".*\n.*link/ether " + MAC_RE_COLON + _tested_arg: bool = False + _iface_arg: bool = False def test(self) -> bool: return check_command("ip") @@ -1300,7 +1285,7 @@ def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 } -DEFAULT_IFACE = "" # type: str +DEFAULT_IFACE: str = "" def get_method_by_name(method_name: str) -> Optional[Type[Method]]: @@ -1395,7 +1380,7 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b platform = PLATFORM if DEBUG >= 4: - meth_strs = ", ".join(m.__name__ for m in METHODS) # type: str + meth_strs = ", ".join(m.__name__ for m in METHODS) log.debug("%d methods available: %s", len(METHODS), meth_strs) # Filter methods by the type of MAC we're looking for, such as "ip" @@ -1413,7 +1398,7 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b return False if DEBUG >= 2: - type_strs = ", ".join(tm.__name__ for tm in type_methods) # type: str + type_strs = ", ".join(tm.__name__ for tm in type_methods) log.debug( "%d type-filtered methods for '%s': %s", len(type_methods), @@ -1440,7 +1425,7 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b ] if DEBUG >= 2: - plat_strs = ", ".join(pm.__name__ for pm in platform_methods) # type: str + plat_strs = ", ".join(pm.__name__ for pm in platform_methods) log.debug( "%d platform-filtered methods for '%s' (method_type='%s'): %s", len(platform_methods), @@ -1468,7 +1453,7 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b for method_class in filtered_methods: method_instance = method_class() # type: Method try: - test_result = method_instance.test() # type: bool + test_result = method_instance.test() except Exception: test_result = False if test_result: @@ -1486,7 +1471,7 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b return False if DEBUG >= 2: - tested_strs = ", ".join(str(ts) for ts in tested_methods) # type: str + tested_strs = ", ".join(str(ts) for ts in tested_methods) log.debug( "%d tested methods for '%s': %s", len(tested_methods), @@ -1740,7 +1725,7 @@ def get_mac_address( mac = None if network_request and (ip or ip6): - send_udp_packet = True # type: bool + send_udp_packet = True # If IPv4, use ArpingHost or CtypesHost if they're available instead # of populating the ARP table. This provides more reliable results diff --git a/pyproject.toml b/pyproject.toml index ed290c8..1b40d7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -258,7 +258,6 @@ select = [ "C4", # flake8-comprehensions "DTZ", # flake8-datetimez "T10", # flake8-debugger - "EXE", # flake8-executable, "ISC", # flake8-implicit-str-concat "G", # flake8-logging-format "PIE", # flake8-pie From f66353cb3d022d2b93f399d79ea6906c42081c22 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 21:23:47 -0600 Subject: [PATCH 024/112] more fix --- getmac/getmac.py | 12 +++++------- tox.ini | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/getmac/getmac.py b/getmac/getmac.py index 0cc7cb2..72aa0b4 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -606,11 +606,9 @@ def test(self) -> bool: return False def get(self, arg: str) -> Optional[str]: - # Convert to bytes on Python 3+ (Fixes GitHub issue #7) - encoded_arg = arg.encode() - try: - inetaddr = ctypes.windll.wsock32.inet_addr(encoded_arg) + # Convert to bytes on Python 3+ (Fixes GitHub issue #7) + inetaddr = ctypes.windll.wsock32.inet_addr(arg.encode()) # type: ignore if inetaddr in (0, -1): raise Exception except Exception: @@ -618,19 +616,19 @@ def get(self, arg: str) -> Optional[str]: # We should be explicit about only accepting ipv4 addresses # and handle any hostname resolution in calling code hostip = socket.gethostbyname(arg) - inetaddr = ctypes.windll.wsock32.inet_addr(hostip) + inetaddr = ctypes.windll.wsock32.inet_addr(hostip) # type: ignore buffer = ctypes.c_buffer(6) addlen = ctypes.c_ulong(ctypes.sizeof(buffer)) # https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-sendarp - send_arp = ctypes.windll.Iphlpapi.SendARP + send_arp = ctypes.windll.Iphlpapi.SendARP # type: ignore if send_arp(inetaddr, 0, ctypes.byref(buffer), ctypes.byref(addlen)) != 0: return None # Convert binary data into a string. macaddr = "" - for intval in struct.unpack("BBBBBB", buffer): + for intval in struct.unpack("BBBBBB", buffer): # type: ignore if intval > 15: replacestr = "0x" else: diff --git a/tox.ini b/tox.ini index bff9137..96768ae 100644 --- a/tox.ini +++ b/tox.ini @@ -37,7 +37,7 @@ platform = windows: win32 allowlist_externals = poetry commands_pre = - poetry install --no-root --sync + poetry install --no-root --sync --no-interaction ; TODO: --benchmark-enable? commands = poetry run pytest \ From e148202679f322e9f1fa4631a0c6fd7f54153d3b Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 21:47:23 -0600 Subject: [PATCH 025/112] remove pypy from CI tests It's become too much of a burden to be worth testing against. Thanks, Poetry. --- .github/workflows/ci.yml | 12 ------------ getmac/getmac.py | 2 +- tox.ini | 7 +------ 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f09882a..c08a3fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,9 +51,6 @@ jobs: - "3.9" - "3.10" - "3.11" - - "pypy3.7" - - "pypy3.8" - - "pypy3.9" architecture: - x86 - x64 @@ -66,15 +63,6 @@ jobs: architecture: x86 - platform: macos-latest architecture: x86 - - platform: macos-latest - python-version: "pypy3.7" - - platform: macos-latest - python-version: "pypy3.8" - - platform: macos-latest - python-version: "pypy3.9" - - platform: windows-latest - python-version: "pypy-3.6" - architecture: x64 steps: - uses: actions/checkout@v3 - name: Install Python diff --git a/getmac/getmac.py b/getmac/getmac.py index 72aa0b4..409c71f 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -601,7 +601,7 @@ class CtypesHost(Method): def test(self) -> bool: try: - return ctypes.windll.wsock32.inet_addr(b"127.0.0.1") > 0 + return ctypes.windll.wsock32.inet_addr(b"127.0.0.1") > 0 # type: ignore except Exception: return False diff --git a/tox.ini b/tox.ini index 96768ae..12ddeae 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{37,38,39,310,311,312,py3}-{linux,macos,windows} +envlist = py{37,38,39,310,311,312}-{linux,macos,windows} skip_missing_interpreters = true [gh-actions] @@ -10,11 +10,6 @@ python = 3.10: py310 3.11: py311 3.12: py312 - pypy3.7: pypy3 - pypy3.8: pypy3 - pypy3.9: pypy3 - pypy3.10: pypy3 - pypy3.11: pypy3 [gh-actions:env] PLATFORM = From 88381e20493d59426f5ec2954e7350dafe8214fb Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 22:02:08 -0600 Subject: [PATCH 026/112] try a different poetry action --- .github/workflows/ci.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c08a3fc..a902be8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,8 @@ jobs: with: python-version: "3.11" - name: Install Poetry - uses: snok/install-poetry@v1 + # uses: snok/install-poetry@v1 + uses: Gr1N/setup-poetry@v8 - name: Install dependencies (poetry) run: poetry install --only=main,lint,ci --no-interaction - name: Run lint checks @@ -32,11 +33,11 @@ jobs: # See: https://github.com/actions/setup-python/issues/532 env: PIP_DISABLE_PIP_VERSION_CHECK: 1 - # Required for snok/install-poetry - # https://github.com/snok/install-poetry/tree/v1/#running-on-windows - defaults: - run: - shell: bash + # # Required for snok/install-poetry + # # https://github.com/snok/install-poetry/tree/v1/#running-on-windows + # defaults: + # run: + # shell: bash strategy: fail-fast: false # Versions available: @@ -71,7 +72,8 @@ jobs: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} - name: Install Poetry - uses: snok/install-poetry@v1 + # uses: snok/install-poetry@v1 + uses: Gr1N/setup-poetry@v8 - name: Display platform and environment information run: | python --version From ca1034ec4136b77087f9f0e80497318c3e57fe5f Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 22:09:40 -0600 Subject: [PATCH 027/112] More fiddling around with CI --- .github/workflows/ci.yml | 13 ++++--------- tox.ini | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a902be8..117d6d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,9 +23,9 @@ jobs: # uses: snok/install-poetry@v1 uses: Gr1N/setup-poetry@v8 - name: Install dependencies (poetry) - run: poetry install --only=main,lint,ci --no-interaction + run: poetry install --no-interaction --only=main,ci,lint - name: Run lint checks - run: poetry run tox -e check + run: poetry run tox --quiet -e check test: name: Run tests runs-on: ${{ matrix.platform }} @@ -33,11 +33,6 @@ jobs: # See: https://github.com/actions/setup-python/issues/532 env: PIP_DISABLE_PIP_VERSION_CHECK: 1 - # # Required for snok/install-poetry - # # https://github.com/snok/install-poetry/tree/v1/#running-on-windows - # defaults: - # run: - # shell: bash strategy: fail-fast: false # Versions available: @@ -82,10 +77,10 @@ jobs: python -c "import os; print('os.name: %s' % str(os.name))" python -c "import platform; print('platform.uname(): %s' % str(platform.uname()))" - name: Install dependencies (poetry) - run: poetry install --no-interaction + run: poetry install --no-interaction --only=main,ci,test # TODO: fix benchmark results (maybe a separate Job?) - name: Run tests - run: poetry run tox + run: poetry run tox --quiet - uses: codecov/codecov-action@v3 with: # codecov is annoying. Not sure why I'm still using it, diff --git a/tox.ini b/tox.ini index 12ddeae..04665dd 100644 --- a/tox.ini +++ b/tox.ini @@ -32,7 +32,7 @@ platform = windows: win32 allowlist_externals = poetry commands_pre = - poetry install --no-root --sync --no-interaction + poetry install --no-root --sync --no-interaction --only=main,ci,lint,test --quiet ; TODO: --benchmark-enable? commands = poetry run pytest \ From f4be00329224f17f8e8e0a028144944c6de0755e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 22:17:11 -0600 Subject: [PATCH 028/112] bad job, BAD! --- .github/workflows/ci.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 117d6d6..973d222 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,6 +59,12 @@ jobs: architecture: x86 - platform: macos-latest architecture: x86 + # Not sure why, but 3.7 on x86 windows is cursed in CI + # It passes (for now), however it's quite slow, + # and I'm ADHD. + - platform: windows-latest + architecture: x86 + python-version: 3.7 steps: - uses: actions/checkout@v3 - name: Install Python @@ -104,18 +110,13 @@ jobs: parallel-finished: true -# TODO: publish -# TODO: need to do this for both "getmac" and "get-mac" packages +# TODO: publish releases using CI # TODO: don't publish pre-releases OR publish them as pre-releases to PyPI - # https://github.com/marketplace/actions/upload-a-build-artifact - # name: "Build and publish a release" - # on: # release: # types: [released] - # jobs: # publish: # name: "📦 Publish Python distributions" From 349ecfdc52f9b973ac94f8856ce244f9407b08b8 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 22:40:00 -0600 Subject: [PATCH 029/112] some test improvements --- tests/test_cli.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 7d42318..45b5890 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,16 +1,16 @@ import platform import sys from subprocess import PIPE, Popen +from typing import List from getmac import __version__, get_mac_address BASE_CMD = [sys.executable, "-m", "getmac"] -def run_cmd(command): - # type: (list) -> str - stdout, _ = Popen(command, stdout=PIPE, stderr=PIPE).communicate() - return stdout.decode("utf-8").strip() +def run_cmd(command: List[str]) -> str: + stdout, stderr = Popen(command, stdout=PIPE, stderr=PIPE).communicate() + return stdout.decode("utf-8").strip() + stderr.decode().strip() def test_cli_main_basic(): @@ -30,11 +30,11 @@ def test_cli_main_invalid_interface(): def test_cli_help(): - assert run_cmd([*BASE_CMD, "--help"]) != "" + assert "usage: getmac" in run_cmd([*BASE_CMD, "--help"]) def test_cli_version(): - assert __version__ in run_cmd([*BASE_CMD, "--version"]) + assert run_cmd([*BASE_CMD, "--version"]).strip().endswith(__version__) def test_cli_multiple_debug_levels(): @@ -52,18 +52,23 @@ def test_cli_no_net(): def test_cli_override_port(): assert ( run_cmd([*BASE_CMD, "-v", "-dd", "-4", "127.0.0.1", "--override-port", "44444"]) - != "" ) def test_cli_localhost(): - assert run_cmd([*BASE_CMD, "-4", "127.0.0.1"]) != "" - assert run_cmd([*BASE_CMD, "-n", "localhost"]) != "" - assert run_cmd([*BASE_CMD, "--no-network-requests", "-4", "127.0.0.1"]) != "" - assert run_cmd([*BASE_CMD, "--no-network-requests", "-n", "localhost"]) != "" + assert run_cmd([*BASE_CMD, "-4", "127.0.0.1"]) + assert run_cmd([*BASE_CMD, "-n", "localhost"]) + assert run_cmd([*BASE_CMD, "--no-network-requests", "-4", "127.0.0.1"]) + assert run_cmd([*BASE_CMD, "--no-network-requests", "-n", "localhost"]) # TODO: figure out how to properly test CLI commands and isolate platform-specific behavior def test_cli_override_platform(): + # TODO: proper test for this plat = platform.system().lower() - assert run_cmd([*BASE_CMD, "-v", "-dd", "--override-platform", plat]) != "" + assert run_cmd([*BASE_CMD, "-v", "-dd", "--override-platform", plat]) + + +def test_cli_force_method(): + # TODO: proper test for this + assert run_cmd([*BASE_CMD, "-v", "-dd", "--force-method", "InvalidMethod"]) From ed9553f9c25cde2ec13a317b3bae166f3892d539 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 2 Jun 2023 22:43:02 -0600 Subject: [PATCH 030/112] remove old video that used 'get-mac' command --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 1a17307..89d3652 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,6 @@ It provides a platform-independent interface to get the MAC addresses of: It provides one function: `get_mac_address()` -[![asciicast](https://asciinema.org/a/rk6dUACUcZY18taCuIBE5Ssus.png)](https://asciinema.org/a/rk6dUACUcZY18taCuIBE5Ssus) - [![asciicast](https://asciinema.org/a/n3insrxfyECch6wxtJEl3LHfv.png)](https://asciinema.org/a/n3insrxfyECch6wxtJEl3LHfv) From 0c1a478cb182d6813f55922905675e3b9978bc20 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 24 Jul 2023 19:57:30 -0600 Subject: [PATCH 031/112] WIP TODO --- getmac/getmac.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/getmac/getmac.py b/getmac/getmac.py index 409c71f..18d7536 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -372,6 +372,12 @@ def get(self, arg: str) -> Optional[str]: return None if data is not None and len(data) > 1: + # TODO: handle flags column, 0x0 entries are incomplete and should be ignored + # https://github.com/GhostofGoes/getmac/issues/76 + # Need to get some samples, probably by doing wifi schennigans + # Not sure if worth addressing for arp command parsers, + # thought maybe it's a use case for Android? (ArpFile no work?) + # Need a space, otherwise a search for 192.168.16.2 # will match 192.168.16.254 if it comes first! return _search(re.escape(arg) + r" .+" + MAC_RE_COLON, data) From 215a563e8691bf0764c0d4dbc1bc0183edb72323 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 24 Aug 2023 20:41:16 -0600 Subject: [PATCH 032/112] Add Poetry badge to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 89d3652..0195134 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![PyPI downloads](https://pepy.tech/badge/getmac)](https://pepy.tech/project/getmac) [![PyPI downloadss of the old name](https://pepy.tech/badge/get-mac)](https://pepy.tech/project/get-mac) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) +[![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/) Pure-Python package to get the MAC address of network interfaces and hosts on the local network. From d43446d51acaf765ce1ed7a07ec361190c5a61dd Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 11 Oct 2023 21:26:38 -0600 Subject: [PATCH 033/112] update TODOs --- docs/TODO.md | 128 +++++++++++++++++++++++++++---------------------- pyproject.toml | 2 - 2 files changed, 71 insertions(+), 59 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 5e74589..6af7aee 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -1,57 +1,30 @@ + # 1.0.0 release -- [x] rename "master" branch to "main" -- [x] Create 0.9.0 branch from master/main so we can submit patch releases if needed + +## Documentation - [ ] Add guide on using the modules API, e.g. registering a new method in `getmac.getmac.METHODS`, etc. -- [ ] [issue #76](https://github.com/GhostofGoes/getmac/issues/76): get_mac_address() is caching an old mac address, no longer present in local ARP - - get_mac_address() is caching an old mac address for a given IP, even when it has timeout from OS ARP table. Only an explicit delete of the ARP entry on the OS make it return '00:00:00:00:00:00' again. - - Fix is to check that the flag != 0x0, which should do the trick, unless there's an edge case that it misses. -- [x] Switch to Poetry for project management -- [x] Support Python 3.10 and 3.11 - - [x] Update pytest (pytest 4, which we were using to support python 2.7, doesn't work with python 3.10) - - [x] add tests + setup.py classifier -- [ ] **API changes** (technically speaking) - - Add argument to `get_mac_address()` to force the platform used (e.g. `platform_override="linux"`) - - Also add CLI argument to configure this - - Add argument to `get_mac_address()` to force a specific method to be used - - Passing a string with the name of a method class (e.g. `"ArpFile"`), this will be dynamically looked up from the list of available methods. This will NOT check if the method works by default! - - Passing a subclass of `getmac.Method` - - Passing an instance of a subclass of `getmac.Method` - - Add a CLI argument to reference class by name - - Add ability to exclude methods. Just remove them from METHODS list so they never get used. Useful for testing specific methods or working around buggy methods. - - Document these features in the README/docs, including the CLI arguments -- [ ] **Consolidate `ip6` argument into `ip` argument.**. Parse based on `::` character vs `.` character if `str` or via `.version == 4`/`.version == 6` for `ipaddress` objects. - - Combine `--ip` and `--ip6` CLI arguments into `--ip` -- [ ] Support `ipaddress` objects, `IPv4Address` and `IPv6Address` -- [ ] Add new method: `get_default_interface()`. This leverages the default interface detection methods to expose a helpful public API. +- [ ] Single page on RTD/publish with GitHub actions built with Sphinx and Furo +- [ ] Update docs/usage examples for `get_mac_address()` +- [ ] Document possible values for `PLATFORM` variable +- [ ] Document Method (and subclass) attributes (use Sphinx "#:" comments) +- [ ] Re-add Man pages (and auto-build them in CI and include in releases and the distributions) +- [ ] Document `get_by_method()` +- [ ] Document `initialize_method_cache()` +- [ ] Auto-generated API docs +- [ ] Add docstrings to all util methods +- Furo, sphinx-autodoc-typehints, sphinx-argparse-cli, sphinx-automodapi, sphinx-copybutton, recommonmark + +## Tests - [ ] Add test to ensure only the expected files make it into the sdist and wheel, no unexpected files -- [ ] Split utils into a separate file -- [ ] move more logic out of `get_mac_address()` into individual methods: - - [ ] interface - - [ ] remote host - - [ ] return data cleanup and validation -- [ ] Raise exceptions on critical failures (stuff that were warnings in 0.9.0), all calls to `_warn_critical()`. -- [ ] Remove all Python "Scripts" from the path, so they don't interfere with the commands we actually want (e.g. "ping"). Document this behavior! -- [ ] [issue 77](https://github.com/GhostofGoes/getmac/issues/77): Feature: get all mac addresses -- [ ] Documentation - - [ ] Single page on RTD/publish with GitHub actions built with Sphinx and Furo - - [ ] Update docs/usage examples for `get_mac_address()` - - [ ] Document possible values for `PLATFORM` variable - - [ ] Document Method (and subclass) attributes (use Sphinx "#:" comments) - - [ ] Re-add Man pages (and auto-build them in CI and include in releases and the distributions) - - [ ] Document `get_by_method()` - - [ ] Document `initialize_method_cache()` - - [ ] Auto-generated API docs - - [ ] Add docstrings to all util methods - - Furo, sphinx-autodoc-typehints, sphinx-argparse-cli, sphinx-automodapi, sphinx-copybutton, recommonmark -- [ ] Support IPv6 hosts: https://www.practicalcodeuse.com/how-to-arp-a-in-ipv6 - [ ] >90% test coverage - refactor tests to use the new system and structure - directly test methods via a `Method.parse()` function - - add `Method.parse()` that handles the parsing of command output. - this would make it *much* easier to test methods -- [ ] implement proper default interface detection on Windows -- [ ] Method-specific loggers? dynamically set logger name based on subclass name, so we don't have to manually set it in the string -- [ ] address all TODOs in the code + - add `Method.parse()` that handles the parsing of command +- [ ] Improve CLI tests to ensure output is what's expected (e.g. ensure `--override-port` logs a warning and the value actually gets overridden) + +## Features +- [ ] Support `ipaddress` objects, `IPv4Address` and `IPv6Address` +- [ ] Add new method: `get_default_interface()`. This leverages the default interface detection methods to expose a helpful public API. - [ ] FreeBSD default interface: `route get default` - [ ] Support NetBSD - platform: `netbsd` @@ -65,31 +38,65 @@ - `netstat` doesn't work with `-e`, but does work with no arguments, `-a` and `-i`. `-n` prevents hostnames from resolving, which is faster. `-i` gives the shortest output (and is fastest), but doesn't give us a MAC address. Providing the interface as an argument also doesn't work to get a MAC (`netstat -a -I e1000g0`). - default interface via `route get default`? - no `ip` command +- [ ] [issue 77](https://github.com/GhostofGoes/getmac/issues/77): Feature: get all mac addresses + - "As I was thinking of adding support to jaraco.net for supporting macOS devices (IP addresses and mac addresses), I thought getmac might be a helpful solution, but as I delved into it, I could see that getmac only returns a single mac, even though there may be multiple on a host. It would be nice if getmac could abstract some of its behaviors, mainly to allow a user to query for all mac addresses represented by the host." + + +## Breaking changes (or potentially breaking) +- [ ] rewrite strings to f-strings +- [ ] CLI: put "override" and other debugging-related arguments into a separate argparse argument group +- [ ] Split getmac.py into separate files for methods, utils, etc. +- [ ] Remove all Python "Scripts" from the path, so they don't interfere with the commands we actually want (e.g. "ping"). Document this behavior! +- [ ] **Consolidate `ip6` argument into `ip` argument.**. Parse based on `::` character vs `.` character if `str` or via `.version == 4`/`.version == 6` for `ipaddress` objects. + - Combine `--ip` and `--ip6` CLI arguments into `--ip` output. this would make it *much* easier to test methods. +- [ ] **API changes** (technically speaking) + - Add argument to `get_mac_address()` to force the platform used (e.g. `platform_override="linux"`) + - Also add CLI argument to configure this + - Add argument to `get_mac_address()` to force a specific method to be used + - Passing a string with the name of a method class (e.g. `"ArpFile"`), this will be dynamically looked up from the list of available methods. This will NOT check if the method works by default! + - Passing a subclass of `getmac.Method` + - Passing an instance of a subclass of `getmac.Method` + - Add a CLI argument to reference class by name + - Add ability to exclude methods. Just remove them from METHODS list so they never get used. Useful for testing specific methods or working around buggy methods. + - Document these features in the README/docs, including the CLI arguments + +## Enhancements/fixes/misc. +- [ ] move more logic out of `get_mac_address()` into individual methods: + - [ ] interface + - [ ] remote host + - [ ] return data cleanup and validation +- [ ] address all TODOs in the code +- [ ] implement proper default interface detection on Windows +- [ ] [issue #76](https://github.com/GhostofGoes/getmac/issues/76): get_mac_address() is caching an old mac address, no longer present in local ARP + - get_mac_address() is caching an old mac address for a given IP, even when it has timeout from OS ARP table. Only an explicit delete of the ARP entry on the OS make it return '00:00:00:00:00:00' again. + - Fix is to check that the flag != 0x0, which should do the trick, unless there's an edge case that it misses. - [ ] Cleanup `ifconfig` methods - [ ] Split `IfconfigOther` into IfconfigWithArg/IfconfigNoArg - [ ] Combine `IfconfigEther` into other Ifconfig methods - [ ] Improve unit test coverage and platform markers - [ ] `IpLinkIface`: improve regex to not need extra portion for no arg -- [ ] New method for "ip addr"? (this would be useful for CentOS and others as a fallback) - [ ] Add new regexes to `IpLinkIface` and improve it's parsing so it's more robust, especially on Android -- [ ] Improve CLI tests to ensure output is what's expected (e.g. ensure `--override-port` logs a warning and the value actually gets overridden) -- [ ] Support IPv6 remote hosts on windows, and IPv4+IPv6 remote hosts on WSL (see "Platform support" section in this document) - [ ] finer-grained platform support identification for methods by versions/releases, e.g. Windows 7 vs 10, Ubuntu 12 vs 20 -- [ ] CLI: put "override" and other debugging-related arguments into a separate argparse argument group -- [ ] Refactor to build a local state of the interfaces on the system, and use that as fallback for default lookup of interface with no name. Could also include MACs for faster lookup of future interface queries. Similar to how `netifaces` works, with a dict with interface infos. Properly address https://github.com/GhostofGoes/getmac/issues/78 -- [x] Replace `flake8-mypy` with proper execution of mypy in tests (the project is dead and archived, https://github.com/ambv/flake8-mypy) + +## Before releasing - [ ] Add a deprecation warning to `get-mac` package, don't publish it for 1.0.0 -- [ ] Add all project contributors to list of Authors in package metadata in `pyproject.toml` (and the documentation) - [ ] Update supported versions table in [SECURITY.md](../SECURITY.md) -## Py3-related stuff for 1.0.0 + +## Done for 1.0.0 +- [x] rename "master" branch to "main" +- [x] Create 0.9.0 branch from master/main so we can submit patch releases if needed - [x] Drop support for python 2.7, 3.4, and 3.5 - [x] BUMP TEST DEPENDENCIES AND PYTEST VERSION TO MODERN TIMES (especially pytest...) -- [ ] rewrite strings to f-strings - [x] Use `pyproject.toml` instead of `setup.py` - [x] update classifiers in setup.py - [x] add inline type annotations for method arguments. remove types from docstrings? - [x] Remove `shutilwhich.py` and `.coveragerc` +- [x] Replace `flake8-mypy` with proper execution of mypy in tests (the project is dead and archived, https://github.com/ambv/flake8-mypy) +- [x] Switch to Poetry for project management +- [x] Support Python 3.10 and 3.11 + - [x] Update pytest (pytest 4, which we were using to support python 2.7, doesn't work with python 3.10) + - [x] add tests + setup.py classifier # Etc @@ -98,6 +105,7 @@ one, move the default fallback logic into the function. - TODO: MAC -> IP. "to_find='mac'"? (create GitHub issue?) - [ ] Move method classes into a separate file + # Bugs or potential issues - [ ] Fix lookup of a IPv4 address of a local interface on Linux - [ ] Unicode handling. UNICODE option needed for non-english locales? (Is LC_ALL working?) @@ -156,6 +164,12 @@ This is going to be a bit more complicated since the highest metric routes are g # Post-1.0.0 +- [ ] Support IPv6 hosts: https://www.practicalcodeuse.com/how-to-arp-a-in-ipv6 +- [ ] Support IPv6 remote hosts on windows, and IPv4+IPv6 remote hosts on WSL (see "Platform support" section in this document) +- [ ] Refactor to build a local state of the interfaces on the system, and use that as fallback for default lookup of interface with no name. Could also include MACs for faster lookup of future interface queries. Similar to how `netifaces` works, with a dict with interface infos. Properly address https://github.com/GhostofGoes/getmac/issues/78 +- [ ] New method for "ip addr"? (this would be useful for CentOS and others as a fallback) +- [ ] Raise exceptions on critical failures (stuff that were warnings in 0.9.0), all calls to `_warn_critical()`. +- [ ] Method-specific loggers? dynamically set logger name based on subclass name, so we don't have to manually set it in the string - [ ] Add typing stubs to [typeshed](https://github.com/python/typeshed) once getmac 1.0.0 is released ([guide](https://github.com/python/typeshed/blob/master/CONTRIBUTING.md)) - [ ] Use `__import__()` or `importlib`? - [ ] Parameterize regexes? (is this any faster?) diff --git a/pyproject.toml b/pyproject.toml index 1b40d7f..77a6345 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,6 @@ build-backend = "poetry.core.masonry.api" [project] name = "getmac" dynamic = ["version", "readme"] -# TODO: add contributors to authors? authors = [ { name="Christopher Goes", email="ghostofgoes@gmail.com" } ] @@ -80,7 +79,6 @@ name = "getmac" version = "1.0.0a0" description = "" license = "MIT" -# TODO: add contributors to authors? authors = [ "Christopher Goes " ] From 7b589390d1b28c153b39516315aa0052bf535953 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 11 Oct 2023 22:17:40 -0600 Subject: [PATCH 034/112] wip --- docs/TODO.md | 2 ++ tests/test_cli.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 6af7aee..0f12a93 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -46,6 +46,7 @@ - [ ] rewrite strings to f-strings - [ ] CLI: put "override" and other debugging-related arguments into a separate argparse argument group - [ ] Split getmac.py into separate files for methods, utils, etc. +- [ ] Refactor how global variables are handled - [ ] Remove all Python "Scripts" from the path, so they don't interfere with the commands we actually want (e.g. "ping"). Document this behavior! - [ ] **Consolidate `ip6` argument into `ip` argument.**. Parse based on `::` character vs `.` character if `str` or via `.version == 4`/`.version == 6` for `ipaddress` objects. - Combine `--ip` and `--ip6` CLI arguments into `--ip` output. this would make it *much* easier to test methods. @@ -61,6 +62,7 @@ - Document these features in the README/docs, including the CLI arguments ## Enhancements/fixes/misc. +- [ ] Properly support WSL2 - [ ] move more logic out of `get_mac_address()` into individual methods: - [ ] interface - [ ] remote host diff --git a/tests/test_cli.py b/tests/test_cli.py index 45b5890..bc70bc0 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -50,8 +50,8 @@ def test_cli_no_net(): def test_cli_override_port(): - assert ( - run_cmd([*BASE_CMD, "-v", "-dd", "-4", "127.0.0.1", "--override-port", "44444"]) + assert run_cmd( + [*BASE_CMD, "-v", "-dd", "-4", "127.0.0.1", "--override-port", "44444"] ) From ff0fe60ca65f668eaf20e8bffe3be1afab138205 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 11 Oct 2023 22:18:03 -0600 Subject: [PATCH 035/112] add variables.py, holds global variables --- getmac/variables.py | 125 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 getmac/variables.py diff --git a/getmac/variables.py b/getmac/variables.py new file mode 100644 index 0000000..db1358a --- /dev/null +++ b/getmac/variables.py @@ -0,0 +1,125 @@ +import logging +import os +import platform +import sys +from typing import Dict, List + + +# TODO: methods to print, dump, etc variables +# TODO: type validation? +class VarsClass: + pass + + +class Settings(VarsClass): + """ + User-configurable settings. + """ + + #: Debugging level. Increased value => more output. 4 is roughly the highest. + DEBUG: int = 0 + + #: UDP port to use for populating the ARP table + PORT: int = 55555 + + #: User-configurable override to force a specific platform + # TODO: This will change to a function argument in 1.0.0 + OVERRIDE_PLATFORM: str = "" + + #: Force a specific method to be used for all lookups + #: Used for debugging and testing + FORCE_METHOD: str = "" + + +class Constants(VarsClass): + """ + Platform identifiers and other constants. + """ + + _UNAME: platform.uname_result = platform.uname() + _SYST: str = _UNAME.system + WINDOWS: bool = _SYST == "Windows" + DARWIN: bool = _SYST == "Darwin" + OPENBSD: bool = _SYST == "OpenBSD" + FREEBSD: bool = _SYST == "FreeBSD" + NETBSD: bool = _SYST == "NetBSD" + SOLARIS: bool = _SYST == "SunOS" + #: Not including Darwin or Solaris as a "BSD" + BSD: bool = OPENBSD or FREEBSD or NETBSD + + #: Windows Subsystem for Linux (WSL) + #: WSL1: abstraction layer remapping Linux syscalls to Windows syscalls + #: WSL2: fancy Linux VM on Hyper-V + WSL1: bool = ( + _SYST == "Linux" + and "Microsoft" in _UNAME.version + and "-WSL2" not in _UNAME.release + ) + WSL2: bool = ( + _SYST == "Linux" + and "Microsoft" not in _UNAME.version + and "-WSL2" in _UNAME.release + ) + LINUX: bool = _SYST == "Linux" and not WSL1 + + #: NOTE: "Linux" methods apply to Android without modifications + #: If there's Android-specific stuff then we can add a platform + #: identifier for it. + ANDROID: bool = ( + hasattr(sys, "getandroidapilevel") or "ANDROID_STORAGE" in os.environ + ) + + #: Generic platform identifier used for filtering methods + # TODO: change to "wsl1", since WSL2 method should just work like normal linux + PLATFORM: str = "wsl" if (LINUX and WSL1) else _SYST.lower() + + MAC_RE_COLON: str = r"([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})" + MAC_RE_DASH: str = r"([0-9a-fA-F]{2}(?:-[0-9a-fA-F]{2}){5})" + #: On OSX, some MACs in arp output may have a single digit instead of two + #: Examples: "18:4f:32:5a:64:5", "14:cc:20:1a:99:0" + #: This can also happen on other platforms, like Solaris + MAC_RE_SHORT: str = r"([0-9a-fA-F]{1,2}(?::[0-9a-fA-F]{1,2}){5})" + + +class Variables(VarsClass): + """ + Things that can change. Essentially most of the global variables in getmac. + """ + + #: Get and cache the configured system PATH on import + #: The process environment does not change after a process is started + PATH: List[str] = os.environ.get("PATH", os.defpath).split(os.pathsep) + PATH_STR: str = os.pathsep.join(PATH) + + #: Use a copy of the environment so we don't + #: modify the process's current environment. + ENV: Dict[str, str] = dict(os.environ) + + #: Cache of commands that have been checked for existence by check_command() + CHECK_COMMAND_CACHE: Dict[str, bool] = {} + + #: Configure logging + log = logging.getLogger("getmac") # type: logging.Logger + if not log.handlers: + log.addHandler(logging.NullHandler()) + + def __init__(self) -> None: + super().__init__() + + self.ENV["LC_ALL"] = "C" # Ensure ASCII output so we parse correctly + + if not Constants.WINDOWS: + self.PATH.extend(("/sbin", "/usr/sbin")) + else: + # TODO: Prevent edge case on Windows where our script "getmac.exe" + # gets added to the path ahead of the actual Windows getmac.exe + # This just handles case where it's in a virtualenv, won't work /w global scripts + self.PATH = [p for p in self.PATH if "\\getmac\\Scripts" not in p] + + # Build the str after modifications are made + self.PATH_STR = os.pathsep.join(self.PATH) + + +settings = Settings() +consts = Constants() +gvars = Variables() From 4cd2edecd2ba37721ad424ab820d7274ce8af6e3 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 12 Oct 2023 23:34:18 -0600 Subject: [PATCH 036/112] Move to new global variables system --- getmac/__init__.py | 2 +- getmac/__main__.py | 13 +- getmac/getmac.py | 376 +++++++++++++++++-------------------------- getmac/variables.py | 9 +- tests/conftest.py | 4 +- tests/test_getmac.py | 41 ++--- 6 files changed, 189 insertions(+), 256 deletions(-) diff --git a/getmac/__init__.py b/getmac/__init__.py index ff8dd56..490550b 100644 --- a/getmac/__init__.py +++ b/getmac/__init__.py @@ -1,3 +1,3 @@ -from .getmac import __version__, get_mac_address # noqa: F401 +from .getmac import __version__, settings, get_mac_address # noqa: F401 __all__ = ["get_mac_address"] diff --git a/getmac/__main__.py b/getmac/__main__.py index 6b2c09b..e20e689 100644 --- a/getmac/__main__.py +++ b/getmac/__main__.py @@ -5,6 +5,7 @@ import sys from . import getmac +from .variables import settings, gvars def main() -> None: @@ -86,20 +87,20 @@ def main() -> None: ) if args.debug: - getmac.DEBUG = args.debug + settings.DEBUG = args.debug if args.override_port: port = int(args.override_port) - getmac.log.debug( - "Using UDP port %d (overriding the default port %d)", port, getmac.PORT + gvars.log.debug( + "Using UDP port %d (overriding the default port %d)", port, settings.PORT ) - getmac.PORT = port + settings.PORT = port if args.override_platform: - getmac.OVERRIDE_PLATFORM = args.override_platform.strip().lower() + settings.OVERRIDE_PLATFORM = args.override_platform.strip().lower() if args.force_method: - getmac.FORCE_METHOD = args.force_method.strip().lower() + settings.FORCE_METHOD = args.force_method.strip().lower() mac = getmac.get_mac_address( interface=args.interface, diff --git a/getmac/getmac.py b/getmac/getmac.py index 18d7536..89f06aa 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -22,112 +22,27 @@ """ import ctypes -import logging import os -import platform import re import shlex import socket import struct -import sys import traceback import warnings from shutil import which from subprocess import DEVNULL, CalledProcessError, check_output from typing import Dict, List, Optional, Set, Tuple, Type, Union -# Configure logging -log = logging.getLogger("getmac") # type: logging.Logger -if not log.handlers: - log.addHandler(logging.NullHandler()) +from .variables import settings, consts, gvars +#: Current version of getmac package __version__ = "1.0.0a0" -# Configurable settings -DEBUG: int = 0 -PORT: int = 55555 - -# Platform identifiers -_UNAME: platform.uname_result = platform.uname() -_SYST: str = _UNAME.system - -WINDOWS: bool = _SYST == "Windows" -DARWIN: bool = _SYST == "Darwin" - -OPENBSD: bool = _SYST == "OpenBSD" -FREEBSD: bool = _SYST == "FreeBSD" -NETBSD: bool = _SYST == "NetBSD" -SOLARIS: bool = _SYST == "SunOS" - -# Not including Darwin or Solaris as a "BSD" -BSD: bool = OPENBSD or FREEBSD or NETBSD - -# Windows Subsystem for Linux (WSL) -WSL: bool = False -LINUX: bool = False -if _SYST == "Linux": - if "Microsoft" in platform.version(): - WSL = True - else: - LINUX = True - -# NOTE: "Linux" methods apply to Android without modifications -# If there's Android-specific stuff then we can add a platform -# identifier for it. -ANDROID: bool = hasattr(sys, "getandroidapilevel") or "ANDROID_STORAGE" in os.environ - -# Generic platform identifier used for filtering methods -PLATFORM: str = _SYST.lower() -if PLATFORM == "linux" and "Microsoft" in platform.version(): - PLATFORM = "wsl" - -# User-configurable override to force a specific platform -# This will change to a function argument in 1.0.0 -OVERRIDE_PLATFORM: str = "" - -# Force a specific method to be used for all lookups -# Used for debugging and testing -FORCE_METHOD: str = "" - -# Get and cache the configured system PATH on import -# The process environment does not change after a process is started -PATH: List[str] = os.environ.get("PATH", os.defpath).split(os.pathsep) -if not WINDOWS: - PATH.extend(("/sbin", "/usr/sbin")) -else: - # TODO: Prevent edge case on Windows where our script "getmac.exe" - # gets added to the path ahead of the actual Windows getmac.exe - # This just handles case where it's in a virtualenv, won't work /w global scripts - PATH = [p for p in PATH if "\\getmac\\Scripts" not in p] -# Build the str after modifications are made -PATH_STR: str = os.pathsep.join(PATH) - -# Use a copy of the environment so we don't -# modify the process's current environment. -ENV: Dict[str, str] = dict(os.environ) -ENV["LC_ALL"] = "C" # Ensure ASCII output so we parse correctly - -# Constants -IP4: int = 0 -IP6: int = 1 -INTERFACE: int = 2 -HOSTNAME: int = 3 - -MAC_RE_COLON: str = r"([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})" -MAC_RE_DASH: str = r"([0-9a-fA-F]{2}(?:-[0-9a-fA-F]{2}){5})" -# On OSX, some MACs in arp output may have a single digit instead of two -# Examples: "18:4f:32:5a:64:5", "14:cc:20:1a:99:0" -# This can also happen on other platforms, like Solaris -MAC_RE_SHORT: str = r"([0-9a-fA-F]{1,2}(?::[0-9a-fA-F]{1,2}){5})" - -# Cache of commands that have been checked for existence by check_command() -CHECK_COMMAND_CACHE: Dict[str, bool] = {} - def check_command(command: str) -> bool: - if command not in CHECK_COMMAND_CACHE: - CHECK_COMMAND_CACHE[command] = bool(which(command, path=PATH_STR)) - return CHECK_COMMAND_CACHE[command] + if command not in gvars.CHECK_COMMAND_CACHE: + gvars.CHECK_COMMAND_CACHE[command] = bool(which(command, path=gvars.PATH_STR)) + return gvars.CHECK_COMMAND_CACHE[command] def check_path(filepath: str) -> bool: @@ -153,12 +68,12 @@ def _clean_mac(mac: Optional[str]) -> Optional[str]: # Fix cases where there are no colons if ":" not in mac and len(mac) == 12: - log.debug("Adding colons to MAC %s", mac) + gvars.log.debug("Adding colons to MAC %s", mac) mac = ":".join(mac[i : i + 2] for i in range(0, len(mac), 2)) # Pad single-character octets with a leading zero (e.g. Darwin's ARP output) elif len(mac) < 17: - log.debug( + gvars.log.debug( "Length of MAC %s is %d, padding single-character octets with zeros", mac, len(mac), @@ -174,10 +89,10 @@ def _clean_mac(mac: Optional[str]) -> Optional[str]: # MAC address should ALWAYS be 17 characters before being returned if len(mac) != 17: - log.warning("MAC address %s is not 17 characters long!", mac) + gvars.log.warning("MAC address %s is not 17 characters long!", mac) mac = None elif mac.count(":") != 5: - log.warning("MAC address %s is missing colon (':') characters", mac) + gvars.log.warning("MAC address %s is missing colon (':') characters", mac) mac = None return mac @@ -187,7 +102,7 @@ def _read_file(filepath: str) -> Optional[str]: with open(filepath) as f: return f.read() except OSError: - log.debug("Could not find file: '%s'", filepath) + gvars.log.debug("Could not find file: '%s'", filepath) return None @@ -195,8 +110,8 @@ def _search( regex: str, text: str, group_index: int = 0, flags: int = 0 ) -> Optional[str]: if not text: - if DEBUG: - log.debug("No text to _search()") + if settings.DEBUG: + gvars.log.debug("No text to _search()") return None match = re.search(regex, text, flags) @@ -207,7 +122,7 @@ def _search( def _popen(command: str, args: str) -> str: - for directory in PATH: + for directory in gvars.PATH: executable = os.path.join(directory, command) if ( os.path.exists(executable) @@ -218,22 +133,24 @@ def _popen(command: str, args: str) -> str: else: executable = command - if DEBUG >= 3: - log.debug("Running: '%s %s'", executable, args) + if settings.DEBUG >= 3: + gvars.log.debug("Running: '%s %s'", executable, args) return _call_proc(executable, args) def _call_proc(executable: str, args: str) -> str: - if WINDOWS: + if consts.WINDOWS: cmd = executable + " " + args # type: ignore else: cmd = [executable, *shlex.split(args)] # type: ignore - output: Union[str, bytes] = check_output(cmd, stderr=DEVNULL, env=ENV) # noqa: S603 + output: Union[str, bytes] = check_output( + cmd, stderr=DEVNULL, env=gvars.ENV # noqa: S603 + ) - if DEBUG >= 4: - log.debug("Output from '%s' command: %s", executable, str(output)) + if settings.DEBUG >= 4: + gvars.log.debug("Output from '%s' command: %s", executable, str(output)) if isinstance(output, bytes): output = output.decode("utf-8") @@ -380,7 +297,7 @@ def get(self, arg: str) -> Optional[str]: # Need a space, otherwise a search for 192.168.16.2 # will match 192.168.16.254 if it comes first! - return _search(re.escape(arg) + r" .+" + MAC_RE_COLON, data) + return _search(re.escape(arg) + r" .+" + consts.MAC_RE_COLON, data) return None @@ -393,14 +310,14 @@ def test(self) -> bool: return check_command("arp") def get(self, arg: str) -> Optional[str]: - regex = r"\(" + re.escape(arg) + r"\)\s+at\s+" + MAC_RE_COLON + regex = r"\(" + re.escape(arg) + r"\)\s+at\s+" + consts.MAC_RE_COLON return _search(regex, _popen("arp", arg)) class ArpOpenbsd(Method): platforms = {"openbsd"} method_type = "ip" - _regex: str = r"[ ]+" + MAC_RE_COLON + _regex: str = r"[ ]+" + consts.MAC_RE_COLON def test(self) -> bool: return check_command("arp") @@ -412,8 +329,8 @@ def get(self, arg: str) -> Optional[str]: class ArpVariousArgs(Method): platforms = {"linux", "darwin", "freebsd", "sunos", "other"} method_type = "ip" - _regex_std: str = r"\)\s+at\s+" + MAC_RE_COLON - _regex_darwin: str = r"\)\s+at\s+" + MAC_RE_SHORT + _regex_std: str = r"\)\s+at\s+" + consts.MAC_RE_COLON + _regex_darwin: str = r"\)\s+at\s+" + consts.MAC_RE_SHORT _args = ( ("", True), # "arp 192.168.1.1" # Linux @@ -452,8 +369,8 @@ def get(self, arg: str) -> Optional[str]: self._good_pair = pair_to_test break except CalledProcessError as ex: - if DEBUG: - log.debug( + if settings.DEBUG: + gvars.log.debug( "ArpVariousArgs pair test failed for (%s, %s): %s", pair_to_test[0], pair_to_test[1], @@ -511,7 +428,7 @@ def test(self) -> bool: return check_command("arp.exe") def get(self, arg: str) -> Optional[str]: - return _search(MAC_RE_DASH, _popen("arp.exe", "-a %s" % arg)) + return _search(consts.MAC_RE_DASH, _popen("arp.exe", "-a %s" % arg)) class ArpingHost(Method): @@ -564,7 +481,7 @@ def get(self, arg: str) -> Optional[str]: command_output = _popen("arping", self._iputils_args % arg) if command_output: return _search( - r" from %s \[(%s)\]" % (re.escape(arg), MAC_RE_COLON), + r" from %s \[(%s)\]" % (re.escape(arg), consts.MAC_RE_COLON), command_output, ) else: @@ -575,8 +492,8 @@ def get(self, arg: str) -> Optional[str]: output = ex.output.decode("utf-8").lower() if "habets" in output or "invalid option" in output: - if DEBUG: - log.debug("Falling back to Habets arping") + if settings.DEBUG: + gvars.log.debug("Falling back to Habets arping") self._is_iputils = False try: return self._call_habets(arg) @@ -662,7 +579,7 @@ def get(self, arg: str) -> Optional[str]: output.partition(arg + " ")[2].partition("lladdr")[2].strip().split()[0] ) except IndexError as ex: - log.debug("IpNeighborShow failed with exception: %s", str(ex)) + gvars.log.debug("IpNeighborShow failed with exception: %s", str(ex)) return None @@ -745,9 +662,9 @@ class GetmacExe(Method): method_type = "iface" _regexes: List[Tuple[str, str]] = [ # Connection Name - (r"\r\n", r".*" + MAC_RE_DASH + r".*\r\n"), + (r"\r\n", r".*" + consts.MAC_RE_DASH + r".*\r\n"), # Network Adapter (the human-readable name) - (r"\r\n.*", r".*" + MAC_RE_DASH + r".*\r\n"), + (r"\r\n.*", r".*" + consts.MAC_RE_DASH + r".*\r\n"), ] _champ: Union[tuple, Tuple[str, str]] = () @@ -764,7 +681,9 @@ def get(self, arg: str) -> Optional[str]: command_output = _popen("getmac.exe", "/NH /V") except CalledProcessError as ex: # This shouldn't cause an exception if it's valid command - log.error("getmac.exe failed, marking unusable. Exception: %s", str(ex)) + gvars.log.error( + "getmac.exe failed, marking unusable. Exception: %s", str(ex) + ) self.unusable = True return None @@ -792,7 +711,9 @@ class IpconfigExe(Method): platforms = {"windows"} method_type = "iface" - _regex: str = r"(?:\n?[^\n]*){1,8}Physical Address[ .:]+" + MAC_RE_DASH + r"\r\n" + _regex: str = ( + r"(?:\n?[^\n]*){1,8}Physical Address[ .:]+" + consts.MAC_RE_DASH + r"\r\n" + ) def test(self) -> bool: return check_command("ipconfig.exe") @@ -854,7 +775,7 @@ def test(self) -> bool: def get(self, arg: str) -> Optional[str]: command_output = _popen("networksetup", "-getmacaddress %s" % arg) - return _search(MAC_RE_COLON, command_output) + return _search(consts.MAC_RE_COLON, command_output) # This only took 15-20 hours of throwing my brain against a wall multiple times @@ -975,11 +896,11 @@ def get(self, arg: str) -> Optional[str]: command_output = _popen("ifconfig", pair_to_test[0]) self._good_pair = list(pair_to_test) # type: ignore if isinstance(self._good_pair[1], str): - self._good_pair[1] += MAC_RE_COLON + self._good_pair[1] += consts.MAC_RE_COLON break except CalledProcessError as ex: - if DEBUG: - log.debug( + if settings.DEBUG: + gvars.log.debug( "IfconfigOther pair test failed for (%s, %s): %s", pair_to_test[0], pair_to_test[1], @@ -998,7 +919,7 @@ def get(self, arg: str) -> Optional[str]: # Handle the two possible search terms if isinstance(self._good_pair[1], tuple): for term in self._good_pair[1]: - regex = term + MAC_RE_COLON + regex = term + consts.MAC_RE_COLON result = _search(re.escape(arg) + regex, command_output) if result: @@ -1018,10 +939,10 @@ class NetstatIface(Method): # ".*?": non-greedy # https://docs.python.org/3/howto/regex.html#greedy-versus-non-greedy _regexes: List[str] = [ - r": .*?ether " + MAC_RE_COLON, - r": .*?HWaddr " + MAC_RE_COLON, + r": .*?ether " + consts.MAC_RE_COLON, + r": .*?HWaddr " + consts.MAC_RE_COLON, # Ubuntu 12.04 and other older kernels - r" .*?Link encap:Ethernet HWaddr " + MAC_RE_COLON, + r" .*?Link encap:Ethernet HWaddr " + consts.MAC_RE_COLON, ] _working_regex: str = "" @@ -1034,7 +955,7 @@ def get(self, arg: str) -> Optional[str]: # therefore have the same output format on the same platform. command_output = _popen("netstat", "-iae") if not command_output: - log.warning("no netstat output, marking unusable") + gvars.log.warning("no netstat output, marking unusable") self.unusable = True return None @@ -1055,22 +976,22 @@ def get(self, arg: str) -> Optional[str]: # TODO: Add to IpLinkIface # TODO: New method for "ip addr"? (this would be useful for CentOS and others as a fallback) -# (r"state UP.*\n.*ether " + MAC_RE_COLON, 0, "ip", ["link","addr"]), -# (r"wlan.*\n.*ether " + MAC_RE_COLON, 0, "ip", ["link","addr"]), -# (r"ether " + MAC_RE_COLON, 0, "ip", ["link","addr"]), +# (r"state UP.*\n.*ether " + consts.MAC_RE_COLON, 0, "ip", ["link","addr"]), +# (r"wlan.*\n.*ether " + consts.MAC_RE_COLON, 0, "ip", ["link","addr"]), +# (r"ether " + consts.MAC_RE_COLON, 0, "ip", ["link","addr"]), # _regexes = ( -# r".*\n.*link/ether " + MAC_RE_COLON, +# r".*\n.*link/ether " + consts.MAC_RE_COLON, # # Android 6.0.1+ (and likely other platforms as well) -# r"state UP.*\n.*ether " + MAC_RE_COLON, -# r"wlan.*\n.*ether " + MAC_RE_COLON, -# r"ether " + MAC_RE_COLON, +# r"state UP.*\n.*ether " + consts.MAC_RE_COLON, +# r"wlan.*\n.*ether " + consts.MAC_RE_COLON, +# r"ether " + consts.MAC_RE_COLON, # ) # type: Tuple[str, str, str, str] class IpLinkIface(Method): platforms = {"linux", "wsl", "android", "other"} method_type = "iface" - _regex: str = r".*\n.*link/ether " + MAC_RE_COLON + _regex: str = r".*\n.*link/ether " + consts.MAC_RE_COLON _tested_arg: bool = False _iface_arg: bool = False @@ -1139,13 +1060,13 @@ def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 if dest == "00000000": return iface_name - if DEBUG: - log.debug( + if settings.DEBUG: + gvars.log.debug( "Failed to find default interface in data from " "'/proc/net/route', no destination of '00000000' was found" ) - elif DEBUG: - log.warning("No data from /proc/net/route") + elif settings.DEBUG: + gvars.log.warning("No data from /proc/net/route") return None @@ -1167,7 +1088,7 @@ def get(self, arg: str = "") -> Optional[str]: .split()[-1] ) except IndexError as ex: # index errors means no default route in output? - log.debug("DefaultIfaceRouteCommand failed for %s: %s", arg, str(ex)) + gvars.log.debug("DefaultIfaceRouteCommand failed for %s: %s", arg, str(ex)) return None @@ -1187,7 +1108,7 @@ def get(self, arg: str = "") -> Optional[str]: try: return output.partition("interface: ")[2].strip().split()[0].strip() except IndexError as ex: - log.debug("DefaultIfaceRouteCommand failed for %s: %s", arg, str(ex)) + gvars.log.debug("DefaultIfaceRouteCommand failed for %s: %s", arg, str(ex)) return None @@ -1204,8 +1125,8 @@ def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 output = _popen("ip", "route list 0/0") if not output: - if DEBUG: - log.debug("DefaultIfaceIpRoute failed: no output") + if settings.DEBUG: + gvars.log.debug("DefaultIfaceIpRoute failed: no output") return None return output.partition("dev")[2].partition("proto")[0].strip() @@ -1268,6 +1189,7 @@ def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 DefaultIfaceFreeBsd, ] # type: List[Type[Method]] +# TODO: move to gvars class? gotta love import loops with type annotations # Primary method to use for a given method type METHOD_CACHE: Dict[str, Optional[Method]] = { "ip4": None, @@ -1289,9 +1211,6 @@ def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 } -DEFAULT_IFACE: str = "" - - def get_method_by_name(method_name: str) -> Optional[Type[Method]]: for method in METHODS: if method.__name__.lower() == method_name.lower(): @@ -1345,7 +1264,7 @@ def _swap_method_fallback(method_type: str, swap_with: str) -> bool: def _warn_critical(err_msg: str) -> None: - log.critical(err_msg) + gvars.log.critical(err_msg) warnings.warn( # noqa: B028 "%s. NOTICE: this warning will likely turn into a raised exception in getmac 1.0.0!" % err_msg, @@ -1364,28 +1283,30 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b (those methods that have the attribute ``network_request`` set to ``True``) """ if METHOD_CACHE.get(method_type): - if DEBUG: - log.debug( + if settings.DEBUG: + gvars.log.debug( "Method cache already initialized for method type '%s'", method_type ) return True - log.debug("Initializing '%s' method cache (platform: '%s')", method_type, PLATFORM) + gvars.log.debug( + "Initializing '%s' method cache (platform: '%s')", method_type, consts.PLATFORM + ) - if OVERRIDE_PLATFORM: - log.warning( + if settings.OVERRIDE_PLATFORM: + gvars.log.warning( "Platform override is set, using '%s' as platform " "instead of detected platform '%s'", - OVERRIDE_PLATFORM, - PLATFORM, + settings.OVERRIDE_PLATFORM, + consts.PLATFORM, ) - platform = OVERRIDE_PLATFORM + platform = settings.OVERRIDE_PLATFORM else: - platform = PLATFORM + platform = consts.PLATFORM - if DEBUG >= 4: + if settings.DEBUG >= 4: meth_strs = ", ".join(m.__name__ for m in METHODS) - log.debug("%d methods available: %s", len(METHODS), meth_strs) + gvars.log.debug("%d methods available: %s", len(METHODS), meth_strs) # Filter methods by the type of MAC we're looking for, such as "ip" # for remote host methods or "iface" for local interface methods. @@ -1401,9 +1322,9 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b _warn_critical("No valid methods matching MAC type '%s'" % method_type) return False - if DEBUG >= 2: + if settings.DEBUG >= 2: type_strs = ", ".join(tm.__name__ for tm in type_methods) - log.debug( + gvars.log.debug( "%d type-filtered methods for '%s': %s", len(type_methods), method_type, @@ -1422,15 +1343,15 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b "No methods for platform '%s'! Your system may not be supported. " "Falling back to platform 'other'." % platform ) - log.warning(warn_msg) + gvars.log.warning(warn_msg) warnings.warn(warn_msg, RuntimeWarning, stacklevel=2) platform_methods = [ method for method in type_methods if "other" in method.platforms ] - if DEBUG >= 2: + if settings.DEBUG >= 2: plat_strs = ", ".join(pm.__name__ for pm in platform_methods) - log.debug( + gvars.log.debug( "%d platform-filtered methods for '%s' (method_type='%s'): %s", len(platform_methods), platform, @@ -1465,8 +1386,8 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b # First successful test goes in the cache if not METHOD_CACHE[method_type]: METHOD_CACHE[method_type] = method_instance - elif DEBUG: - log.debug("Test failed for method '%s'", str(method_instance)) + elif settings.DEBUG: + gvars.log.debug("Test failed for method '%s'", str(method_instance)) if not tested_methods: _warn_critical( @@ -1474,9 +1395,9 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b ) return False - if DEBUG >= 2: + if settings.DEBUG >= 2: tested_strs = ", ".join(str(ts) for ts in tested_methods) - log.debug( + gvars.log.debug( "%d tested methods for '%s': %s", len(tested_methods), method_type, @@ -1489,27 +1410,27 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b FALLBACK_CACHE[method_type] = tested_methods - if DEBUG: - log.debug( + if settings.DEBUG: + gvars.log.debug( "Current method cache: %s", str({k: str(v) for k, v in METHOD_CACHE.items()}), ) - log.debug( + gvars.log.debug( "Current fallback cache: %s", str({k: str(v) for k, v in FALLBACK_CACHE.items()}), ) - log.debug("Finished initializing '%s' method cache", method_type) + gvars.log.debug("Finished initializing '%s' method cache", method_type) return True def _remove_unusable(method: Method, method_type: str) -> Optional[Method]: if not FALLBACK_CACHE[method_type]: - log.warning("No fallback method for unusable method '%s'!", str(method)) + gvars.log.warning("No fallback method for unusable method '%s'!", str(method)) METHOD_CACHE[method_type] = None else: METHOD_CACHE[method_type] = FALLBACK_CACHE[method_type].pop(0) - log.warning( + gvars.log.warning( "Falling back to '%s' for unusable method '%s'", str(METHOD_CACHE[method_type]), str(method), @@ -1526,8 +1447,8 @@ def _attempt_method_get(method: Method, method_type: str, arg: str) -> Optional[ _warn_critical("No usable methods found for MAC type '%s'" % method_type) return None - if DEBUG: - log.debug( + if settings.DEBUG: + gvars.log.debug( "Attempting get() (method='%s', method_type='%s', arg='%s')", str(method), method_type, @@ -1547,7 +1468,7 @@ def _attempt_method_get(method: Method, method_type: str, arg: str) -> Optional[ # Methods where an exit code of 1 makes it invalid should handle the # CalledProcessError, inspect the return code, and set self.unusable = True if ex.returncode != 1: - log.warning( + gvars.log.warning( "Cached Method '%s' failed for '%s' lookup with process exit " "code '%d' != 1, marking unusable. Exception: %s", str(method), @@ -1557,7 +1478,7 @@ def _attempt_method_get(method: Method, method_type: str, arg: str) -> Optional[ ) method.unusable = True except Exception as ex: - log.warning( + gvars.log.warning( "Cached Method '%s' failed for '%s' lookup with unhandled exception: %s", str(method), method_type, @@ -1592,21 +1513,25 @@ def get_by_method( (those methods that have the attribute ``network_request`` set to ``True``) """ if not arg and method_type != "default_iface": - log.error("Empty arg for method '%s' (raw value: %s)", method_type, repr(arg)) + gvars.log.error( + "Empty arg for method '%s' (raw value: %s)", method_type, repr(arg) + ) return None - if FORCE_METHOD: - log.warning( + if settings.FORCE_METHOD: + gvars.log.warning( "Forcing method '%s' to be used for '%s' lookup (arg: '%s')", - FORCE_METHOD, + settings.FORCE_METHOD, method_type, arg, ) - forced_method = get_method_by_name(FORCE_METHOD) + forced_method = get_method_by_name(settings.FORCE_METHOD) if not forced_method: - log.error("Invalid FORCE_METHOD method name '%s'", FORCE_METHOD) + gvars.log.error( + "Invalid settings.FORCE_METHOD method name '%s'", settings.FORCE_METHOD + ) return None return forced_method().get(arg) @@ -1616,7 +1541,7 @@ def get_by_method( if not method: # Initialize the cache if it hasn't been already if not initialize_method_cache(method_type, network_request): - log.error( + gvars.log.error( "Failed to initialize method cache for method '%s' (arg: '%s')", method_type, arg, @@ -1626,7 +1551,7 @@ def get_by_method( method = METHOD_CACHE[method_type] if not method: - log.error( + gvars.log.error( "Initialization failed for method '%s'. It may not be supported " "on this platform or another issue occurred.", method_type, @@ -1639,8 +1564,8 @@ def get_by_method( result = _attempt_method_get(method, method_type, arg) # Log normal get() failures if debugging is enabled - if DEBUG and not result: - log.debug("Method '%s' failed for '%s' lookup", str(method), method_type) + if settings.DEBUG and not result: + gvars.log.debug("Method '%s' failed for '%s' lookup", str(method), method_type) return result @@ -1695,7 +1620,7 @@ def get_mac_address( found or there was an error. """ - if DEBUG: + if settings.DEBUG: import timeit start_time = timeit.default_timer() @@ -1710,20 +1635,20 @@ def get_mac_address( # TODO: can this return a IPv6 address? If so, handle that! ip = socket.gethostbyname(hostname) except Exception as ex: - log.error("Could not resolve hostname '%s': %s", hostname, ex) - if DEBUG: - log.debug(traceback.format_exc()) + gvars.log.error("Could not resolve hostname '%s': %s", hostname, ex) + if settings.DEBUG: + gvars.log.debug(traceback.format_exc()) return None if ip6: if not socket.has_ipv6: - log.error( + gvars.log.error( "Cannot get the MAC address of a IPv6 host: " "IPv6 is not supported on this system" ) return None elif ":" not in ip6: - log.error("Invalid IPv6 address (no ':'): %s", ip6) + gvars.log.error("Invalid IPv6 address (no ':'): %s", ip6) return None mac = None @@ -1741,7 +1666,7 @@ def get_mac_address( # If ArpFile succeeds, just use that, since it's # significantly faster than arping (file read vs. # spawning a process). - if not FORCE_METHOD or FORCE_METHOD.lower() == "arpfile": + if not settings.FORCE_METHOD or settings.FORCE_METHOD.lower() == "arpfile": af_meth = get_instance_from_cache("ip4", "ArpFile") if af_meth: mac = _attempt_method_get(af_meth, "ip4", ip) @@ -1753,7 +1678,10 @@ def get_mac_address( if not mac: for arp_meth in ["CtypesHost", "ArpingHost"]: - if FORCE_METHOD and FORCE_METHOD.lower() != arp_meth: + if ( + settings.FORCE_METHOD + and settings.FORCE_METHOD.lower() != arp_meth + ): continue if arp_meth == str(METHOD_CACHE["ip4"]): @@ -1768,11 +1696,11 @@ def get_mac_address( # Populate the ARP table by sending an empty UDP packet to a high port if send_udp_packet and not mac: - if DEBUG: - log.debug( + if settings.DEBUG: + gvars.log.debug( "Attempting to populate ARP table with UDP packet to %s:%d", ip if ip else ip6, - PORT, + settings.PORT, ) if ip: @@ -1782,17 +1710,17 @@ def get_mac_address( try: if ip: - sock.sendto(b"", (ip, PORT)) + sock.sendto(b"", (ip, settings.PORT)) else: - sock.sendto(b"", (ip6, PORT)) + sock.sendto(b"", (ip6, settings.PORT)) except Exception: - log.error("Failed to send ARP table population packet") - if DEBUG: - log.debug(traceback.format_exc()) + gvars.log.error("Failed to send ARP table population packet") + if settings.DEBUG: + gvars.log.debug(traceback.format_exc()) finally: sock.close() - elif DEBUG: - log.debug( + elif settings.DEBUG: + gvars.log.debug( "Not sending UDP packet, using network request method '%s' instead", str(METHOD_CACHE["ip4"]), ) @@ -1807,41 +1735,39 @@ def get_mac_address( mac = get_by_method("iface", interface) else: # Default to searching for interface # Default to finding MAC of the interface with the default route - if WINDOWS and network_request: + if consts.WINDOWS and network_request: default_iface_ip = _fetch_ip_using_dns() mac = get_by_method("ip4", default_iface_ip) - elif WINDOWS: + elif consts.WINDOWS: # TODO: implement proper default interface detection on windows # (add a Method subclass to implement DefaultIface on Windows) mac = get_by_method("iface", "Ethernet") else: - global DEFAULT_IFACE - - if not DEFAULT_IFACE: - DEFAULT_IFACE = get_by_method("default_iface") # type: ignore + if not gvars.DEFAULT_IFACE: + gvars.DEFAULT_IFACE = get_by_method("default_iface") # type: ignore - if DEFAULT_IFACE: - DEFAULT_IFACE = str(DEFAULT_IFACE).strip() + if gvars.DEFAULT_IFACE: + gvars.DEFAULT_IFACE = str(gvars.DEFAULT_IFACE).strip() # TODO: better fallback if default iface lookup fails - if not DEFAULT_IFACE and BSD: - DEFAULT_IFACE = "em0" - elif not DEFAULT_IFACE and DARWIN: # OSX, maybe? - DEFAULT_IFACE = "en0" - elif not DEFAULT_IFACE: - DEFAULT_IFACE = "eth0" + if not gvars.DEFAULT_IFACE and consts.BSD: + gvars.DEFAULT_IFACE = "em0" + elif not gvars.DEFAULT_IFACE and consts.DARWIN: # OSX, maybe? + gvars.DEFAULT_IFACE = "en0" + elif not gvars.DEFAULT_IFACE: + gvars.DEFAULT_IFACE = "eth0" - mac = get_by_method("iface", DEFAULT_IFACE) + mac = get_by_method("iface", gvars.DEFAULT_IFACE) # TODO: hack to fallback to loopback if lookup fails if not mac: mac = get_by_method("iface", "lo") - log.debug("Raw MAC found: %s", mac) + gvars.log.debug("Raw MAC found: %s", mac) # Log how long it took - if DEBUG: + if settings.DEBUG: duration = timeit.default_timer() - start_time - log.debug("getmac took %.4f seconds", duration) + gvars.log.debug("getmac took %.4f seconds", duration) return _clean_mac(mac) diff --git a/getmac/variables.py b/getmac/variables.py index db1358a..0db466f 100644 --- a/getmac/variables.py +++ b/getmac/variables.py @@ -88,6 +88,7 @@ class Variables(VarsClass): #: Get and cache the configured system PATH on import #: The process environment does not change after a process is started + # TODO: move to Constants? PATH: List[str] = os.environ.get("PATH", os.defpath).split(os.pathsep) PATH_STR: str = os.pathsep.join(PATH) @@ -100,12 +101,16 @@ class Variables(VarsClass): #: Configure logging log = logging.getLogger("getmac") # type: logging.Logger - if not log.handlers: - log.addHandler(logging.NullHandler()) + + #: Default interface + DEFAULT_IFACE: str = "" def __init__(self) -> None: super().__init__() + if not self.log.handlers: + self.log.addHandler(logging.NullHandler()) + self.ENV["LC_ALL"] = "C" # Ensure ASCII output so we parse correctly if not Constants.WINDOWS: diff --git a/tests/conftest.py b/tests/conftest.py index 9f7d90e..2a8b9de 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,9 +3,9 @@ import pytest -import getmac +from getmac.variables import settings -getmac.getmac.DEBUG = 4 +settings.DEBUG = 4 @pytest.fixture() diff --git a/tests/test_getmac.py b/tests/test_getmac.py index 2983051..76e436e 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -4,6 +4,7 @@ import pytest from getmac import get_mac_address, getmac +from getmac.variables import settings, consts, gvars MAC_RE_COLON = r"([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})" @@ -51,7 +52,7 @@ def test_search(get_sample): def test_popen(mocker): - mocker.patch("getmac.getmac.PATH", []) + mocker.patch.object(gvars, "PATH", []) m = mocker.patch("getmac.getmac._call_proc", return_value="SUCCESS") assert getmac._popen("TESTCMD", "ARGS") == "SUCCESS" m.assert_called_once_with("TESTCMD", "ARGS") @@ -59,14 +60,14 @@ def test_popen(mocker): def test_call_proc(mocker): mocker.patch("getmac.getmac.DEVNULL", "DEVNULL") - mocker.patch("getmac.getmac.ENV", "ENV") + mocker.patch.object(gvars, "ENV", "ENV") - mocker.patch("getmac.getmac.WINDOWS", True) + mocker.patch.object(consts, "WINDOWS", True) m = mocker.patch("getmac.getmac.check_output", return_value="WINSUCCESS") assert getmac._call_proc("CMD", "arg") == "WINSUCCESS" m.assert_called_once_with("CMD arg", stderr="DEVNULL", env="ENV") - mocker.patch("getmac.getmac.WINDOWS", False) + mocker.patch.object(consts, "WINDOWS", False) m = mocker.patch("getmac.getmac.check_output", return_value="YAY") assert getmac._call_proc("CMD", "arg1 arg2") == "YAY" m.assert_called_once_with(["CMD", "arg1", "arg2"], stderr="DEVNULL", env="ENV") @@ -133,7 +134,7 @@ def test_initialize_method_cache_valid_types(mocker, method_type): {"ip4": None, "ip6": None, "iface": None, "default_iface": None}, ) mocker.patch("getmac.getmac.FALLBACK_CACHE", {}) - mocker.patch("getmac.getmac.PLATFORM", "linux") + mocker.patch.object(consts, "PLATFORM", "linux") assert getmac.initialize_method_cache(method_type) assert getmac.METHOD_CACHE[method_type] is not None if method_type in ["ip4", "ip6"]: @@ -146,7 +147,7 @@ def test_initialize_method_cache_initialized(mocker): {"ip4": getmac.ArpFile(), "ip6": None, "iface": None, "default_iface": None}, ) mocker.patch("getmac.getmac.FALLBACK_CACHE", {}) - mocker.patch("getmac.getmac.PLATFORM", "linux") + mocker.patch.object(consts, "PLATFORM", "linux") assert getmac.initialize_method_cache("ip4") assert str(getmac.METHOD_CACHE["ip4"]) == "ArpFile" assert isinstance(getmac.METHOD_CACHE["ip4"], getmac.Method) @@ -158,7 +159,7 @@ def test_initialize_method_cache_bad_type(mocker): {"ip4": None, "ip6": None, "iface": None, "default_iface": None}, ) mocker.patch("getmac.getmac.FALLBACK_CACHE", {}) - mocker.patch("getmac.getmac.PLATFORM", "linux") + mocker.patch.object(consts, "PLATFORM", "linux") with pytest.warns(RuntimeWarning): assert not getmac.initialize_method_cache("invalid_method_type") with pytest.warns(RuntimeWarning): @@ -172,12 +173,12 @@ def test_initialize_method_cache_platform_override(mocker): {"ip4": None, "ip6": None, "iface": None, "default_iface": None}, ) mocker.patch("getmac.getmac.FALLBACK_CACHE", {}) - mocker.patch("getmac.getmac.PLATFORM", "windows") - mocker.patch("getmac.getmac.OVERRIDE_PLATFORM", "darwin") + mocker.patch.object(consts, "PLATFORM", "windows") + mocker.patch.object(settings, "OVERRIDE_PLATFORM", "darwin") mocker.patch("getmac.getmac.check_command", return_value=True) assert getmac.initialize_method_cache("iface") - assert getmac.OVERRIDE_PLATFORM == "darwin" - assert getmac.PLATFORM == "windows" + assert settings.OVERRIDE_PLATFORM == "darwin" + assert consts.PLATFORM == "windows" assert isinstance(getmac.METHOD_CACHE["iface"], getmac.IfconfigEther) @@ -187,11 +188,11 @@ def test_initialize_method_cache_no_network_request(mocker): {"ip4": None, "ip6": None, "iface": None, "default_iface": None}, ) mocker.patch("getmac.getmac.FALLBACK_CACHE", {}) - mocker.patch("getmac.getmac.PLATFORM", "linux") + mocker.patch.object(consts, "PLATFORM", "linux") mocker.patch("getmac.getmac.check_command", return_value=True) mocker.patch("getmac.getmac.check_path", return_value=True) assert getmac.initialize_method_cache("ip4", network_request=False) - assert getmac.PLATFORM == "linux" + assert consts.PLATFORM == "linux" assert isinstance(getmac.METHOD_CACHE["ip4"], getmac.ArpFile) @@ -252,22 +253,22 @@ def test_get_by_method_errors(mocker): }, ) mocker.patch("getmac.getmac._read_file", return_value="") - mocker.patch("getmac.getmac.DEBUG", 1) + mocker.patch.object(settings, "DEBUG", 1) assert getmac.get_by_method("iface", arg="ens33") is None def test_get_by_method_force_method(mocker): - mocker.patch("getmac.getmac.FORCE_METHOD", "testing123") + mocker.patch.object(settings, "FORCE_METHOD", "testing123") assert getmac.get_by_method("iface", arg="some_arg") is None mocker.patch("getmac.getmac._read_file", return_value="00:0c:29:b5:72:37\n") - mocker.patch("getmac.getmac.FORCE_METHOD", "SysIfaceFile") + mocker.patch.object(settings, "FORCE_METHOD", "SysIfaceFile") assert getmac.get_by_method("iface", "ens33") == "00:0c:29:b5:72:37\n" def test_get_mac_address_force_method(mocker): mocker.patch("getmac.getmac._read_file", return_value="00:0c:29:b5:72:37\n") - mocker.patch("getmac.getmac.FORCE_METHOD", "SysIfaceFile") + mocker.patch.object(settings, "FORCE_METHOD", "SysIfaceFile") assert getmac.get_mac_address(interface="ens33") == "00:0c:29:b5:72:37" @@ -315,7 +316,7 @@ def test_get_mac_address_hostname(mocker): def test_get_mac_address_default_args_windows_net_request_true(mocker): - mocker.patch("getmac.getmac.WINDOWS", True) + mocker.patch.object(consts, "WINDOWS", True) mocker.patch("getmac.getmac.get_by_method", return_value="00:FF:17:15:F8:C8") assert getmac.get_mac_address(network_request=False) == "00:ff:17:15:f8:c8" getmac.get_by_method.assert_called_once_with("iface", "Ethernet") @@ -327,8 +328,8 @@ def test_get_mac_address_default_args_windows_net_request_true(mocker): def test_get_mac_address_default_args_fallback_global(mocker): - mocker.patch("getmac.getmac.WINDOWS", False) - mocker.patch("getmac.getmac.DEFAULT_IFACE", "eth0") + mocker.patch.object(consts, "WINDOWS", False) + mocker.patch.object(gvars, "DEFAULT_IFACE", "eth0") mocker.patch("getmac.getmac.get_by_method", return_value="08:00:27:e8:81:6f") assert getmac.get_mac_address() == "08:00:27:e8:81:6f" getmac.get_by_method.assert_called_once_with("iface", "eth0") From bec556f3ff43427c08677f446463f08d2f10fa57 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 12 Oct 2023 23:39:28 -0600 Subject: [PATCH 037/112] docstrings --- docs/TODO.md | 3 ++- getmac/getmac.py | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 0f12a93..816687e 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -46,8 +46,9 @@ - [ ] rewrite strings to f-strings - [ ] CLI: put "override" and other debugging-related arguments into a separate argparse argument group - [ ] Split getmac.py into separate files for methods, utils, etc. -- [ ] Refactor how global variables are handled +- [x] Refactor how global variables are handled - [ ] Remove all Python "Scripts" from the path, so they don't interfere with the commands we actually want (e.g. "ping"). Document this behavior! +- [ ] Replace the `UuidArpGetNode` method. It calls 3 commands and is quite inefficient, and doesn't exist in Python 3.9+. We should just take the methods and use directly. - [ ] **Consolidate `ip6` argument into `ip` argument.**. Parse based on `::` character vs `.` character if `str` or via `.version == 4`/`.version == 6` for `ipaddress` objects. - Combine `--ip` and `--ip6` CLI arguments into `--ip` output. this would make it *much* easier to test methods. - [ ] **API changes** (technically speaking) diff --git a/getmac/getmac.py b/getmac/getmac.py index 89f06aa..05e6d8a 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -40,17 +40,26 @@ def check_command(command: str) -> bool: + """ + Check if a command exists using `shutil.which()`. The result of the check + is cached in a global dict to speed up subsequent lookups. + """ if command not in gvars.CHECK_COMMAND_CACHE: gvars.CHECK_COMMAND_CACHE[command] = bool(which(command, path=gvars.PATH_STR)) return gvars.CHECK_COMMAND_CACHE[command] def check_path(filepath: str) -> bool: + """ + Check if the file pointed to by `filepath` exists and is readable. + """ return os.path.exists(filepath) and os.access(filepath, os.R_OK) def _clean_mac(mac: Optional[str]) -> Optional[str]: - """Check and format a string result to be lowercase colon-separated MAC.""" + """ + Check and format a string result to be lowercase colon-separated MAC. + """ if mac is None: return None @@ -1613,7 +1622,7 @@ def get_mac_address( MAC of a remote host. If the ``arping`` command is available, this will be used. If not, a UDP packet will be sent to the remote host to populate the ARP/NDP tables for IPv4/IPv6. The port this packet is sent to can - be configured using the module variable ``getmac.PORT``. + be configured using the setting ``getmac.settings.PORT``. Returns: Lowercase colon-separated MAC address, or :obj:`None` if one could not be From 0836fd3df69df073aed4a5733b41767932c1dbd1 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 13 Oct 2023 00:01:45 -0600 Subject: [PATCH 038/112] Convert old-style strings to f-strings --- docs/TODO.md | 2 +- getmac/__main__.py | 2 +- getmac/getmac.py | 169 +++++++++++++++++---------------------------- 3 files changed, 67 insertions(+), 106 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 816687e..68c2279 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -43,7 +43,7 @@ ## Breaking changes (or potentially breaking) -- [ ] rewrite strings to f-strings +- [x] rewrite strings to f-strings - [ ] CLI: put "override" and other debugging-related arguments into a separate argparse argument group - [ ] Split getmac.py into separate files for methods, utils, etc. - [x] Refactor how global variables are handled diff --git a/getmac/__main__.py b/getmac/__main__.py index e20e689..dfad52c 100644 --- a/getmac/__main__.py +++ b/getmac/__main__.py @@ -15,7 +15,7 @@ def main() -> None: "interfaces or remote hosts on the LAN", ) parser.add_argument( - "--version", action="version", version="getmac %s" % getmac.__version__ + "--version", action="version", version=f"getmac {getmac.__version__}" ) parser.add_argument( "-v", "--verbose", action="store_true", help="Enable output messages" diff --git a/getmac/getmac.py b/getmac/getmac.py index 05e6d8a..8b5b22a 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -77,15 +77,13 @@ def _clean_mac(mac: Optional[str]) -> Optional[str]: # Fix cases where there are no colons if ":" not in mac and len(mac) == 12: - gvars.log.debug("Adding colons to MAC %s", mac) + gvars.log.debug(f"Adding colons to MAC {mac}") mac = ":".join(mac[i : i + 2] for i in range(0, len(mac), 2)) # Pad single-character octets with a leading zero (e.g. Darwin's ARP output) elif len(mac) < 17: gvars.log.debug( - "Length of MAC %s is %d, padding single-character octets with zeros", - mac, - len(mac), + f"Length of MAC {mac} is {len(mac)}, padding single-character octets with zeros" ) parts = mac.split(":") new_mac = [] @@ -98,10 +96,10 @@ def _clean_mac(mac: Optional[str]) -> Optional[str]: # MAC address should ALWAYS be 17 characters before being returned if len(mac) != 17: - gvars.log.warning("MAC address %s is not 17 characters long!", mac) + gvars.log.warning(f"MAC address {mac} is not 17 characters long!") mac = None elif mac.count(":") != 5: - gvars.log.warning("MAC address %s is missing colon (':') characters", mac) + gvars.log.warning(f"MAC address {mac} is missing colon (':') characters") mac = None return mac @@ -111,7 +109,7 @@ def _read_file(filepath: str) -> Optional[str]: with open(filepath) as f: return f.read() except OSError: - gvars.log.debug("Could not find file: '%s'", filepath) + gvars.log.debug(f"Could not find file: '{filepath}'") return None @@ -143,7 +141,7 @@ def _popen(command: str, args: str) -> str: executable = command if settings.DEBUG >= 3: - gvars.log.debug("Running: '%s %s'", executable, args) + gvars.log.debug(f"Running: '{executable} {args}'") return _call_proc(executable, args) @@ -159,7 +157,7 @@ def _call_proc(executable: str, args: str) -> str: ) if settings.DEBUG >= 4: - gvars.log.debug("Output from '%s' command: %s", executable, str(output)) + gvars.log.debug(f"Output from '{executable}' command: {str(output)}") if isinstance(output, bytes): output = output.decode("utf-8") @@ -380,10 +378,8 @@ def get(self, arg: str) -> Optional[str]: except CalledProcessError as ex: if settings.DEBUG: gvars.log.debug( - "ArpVariousArgs pair test failed for (%s, %s): %s", - pair_to_test[0], - pair_to_test[1], - str(ex), + f"ArpVariousArgs pair test failed for " + f"({pair_to_test[0]}, {pair_to_test[1]}): {ex}" ) if not self._good_pair: @@ -437,7 +433,7 @@ def test(self) -> bool: return check_command("arp.exe") def get(self, arg: str) -> Optional[str]: - return _search(consts.MAC_RE_DASH, _popen("arp.exe", "-a %s" % arg)) + return _search(consts.MAC_RE_DASH, _popen("arp.exe", f"-a {arg}")) class ArpingHost(Method): @@ -471,8 +467,8 @@ class ArpingHost(Method): method_type = "ip4" network_request = True _is_iputils: bool = True - _habets_args: str = "-r -C 1 -c 1 %s" - _iputils_args: str = "-f -c 1 %s" + _habets_args: str = "-r -C 1 -c 1" + _iputils_args: str = "-f -c 1" def test(self) -> bool: return check_command("arping") @@ -487,7 +483,7 @@ def get(self, arg: str) -> Optional[str]: # then re-try with Habets args. try: if self._is_iputils: - command_output = _popen("arping", self._iputils_args % arg) + command_output = _popen("arping", f"{self._iputils_args} {arg}") if command_output: return _search( r" from %s \[(%s)\]" % (re.escape(arg), consts.MAC_RE_COLON), @@ -512,7 +508,7 @@ def get(self, arg: str) -> Optional[str]: return None def _call_habets(self, arg: str) -> Optional[str]: - command_output = _popen("arping", self._habets_args % arg) + command_output = _popen("arping", f"{self._habets_args} {arg}") if command_output: return command_output.strip() else: @@ -578,7 +574,7 @@ def test(self) -> bool: return check_command("ip") def get(self, arg: str) -> Optional[str]: - output = _popen("ip", "neighbor show %s" % arg) + output = _popen("ip", f"neighbor show {arg}") if not output: return None @@ -588,7 +584,7 @@ def get(self, arg: str) -> Optional[str]: output.partition(arg + " ")[2].partition("lladdr")[2].strip().split()[0] ) except IndexError as ex: - gvars.log.debug("IpNeighborShow failed with exception: %s", str(ex)) + gvars.log.debug(f"IpNeighborShow failed with exception: {ex}") return None @@ -690,9 +686,7 @@ def get(self, arg: str) -> Optional[str]: command_output = _popen("getmac.exe", "/NH /V") except CalledProcessError as ex: # This shouldn't cause an exception if it's valid command - gvars.log.error( - "getmac.exe failed, marking unusable. Exception: %s", str(ex) - ) + gvars.log.error(f"getmac.exe failed, marking unusable. Exception: {ex}") self.unusable = True return None @@ -752,7 +746,7 @@ def test(self) -> bool: def get(self, arg: str) -> Optional[str]: command_output = _popen( "wmic.exe", - 'nic where "NetConnectionID = \'%s\'" get "MACAddress" /value' % arg, + f'nic where "NetConnectionID = \'{arg}\'" get "MACAddress" /value', ) # Negative: "No Instance(s) Available" @@ -783,7 +777,7 @@ def test(self) -> bool: return check_command("networksetup") def get(self, arg: str) -> Optional[str]: - command_output = _popen("networksetup", "-getmacaddress %s" % arg) + command_output = _popen("networksetup", f"-getmacaddress {arg}") return _search(consts.MAC_RE_COLON, command_output) @@ -910,10 +904,8 @@ def get(self, arg: str) -> Optional[str]: except CalledProcessError as ex: if settings.DEBUG: gvars.log.debug( - "IfconfigOther pair test failed for (%s, %s): %s", - pair_to_test[0], - pair_to_test[1], - str(ex), + f"IfconfigOther pair test failed for " + f"({pair_to_test[0]}, {pair_to_test[1]}): {ex}" ) if not self._good_pair: @@ -1097,7 +1089,7 @@ def get(self, arg: str = "") -> Optional[str]: .split()[-1] ) except IndexError as ex: # index errors means no default route in output? - gvars.log.debug("DefaultIfaceRouteCommand failed for %s: %s", arg, str(ex)) + gvars.log.debug(f"DefaultIfaceRouteCommand failed for {arg}: {ex}") return None @@ -1117,7 +1109,7 @@ def get(self, arg: str = "") -> Optional[str]: try: return output.partition("interface: ")[2].strip().split()[0].strip() except IndexError as ex: - gvars.log.debug("DefaultIfaceRouteCommand failed for %s: %s", arg, str(ex)) + gvars.log.debug(f"DefaultIfaceRouteCommand failed for {arg}: {ex}") return None @@ -1275,8 +1267,7 @@ def _swap_method_fallback(method_type: str, swap_with: str) -> bool: def _warn_critical(err_msg: str) -> None: gvars.log.critical(err_msg) warnings.warn( # noqa: B028 - "%s. NOTICE: this warning will likely turn into a raised exception in getmac 1.0.0!" - % err_msg, + f"{err_msg}. NOTICE: this warning my turn into a raised exception in a future release", RuntimeWarning, ) @@ -1294,20 +1285,18 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b if METHOD_CACHE.get(method_type): if settings.DEBUG: gvars.log.debug( - "Method cache already initialized for method type '%s'", method_type + f"Method cache already initialized for method type '{method_type}'" ) return True gvars.log.debug( - "Initializing '%s' method cache (platform: '%s')", method_type, consts.PLATFORM + f"Initializing '{method_type}' method cache (platform: '{consts.PLATFORM}')" ) if settings.OVERRIDE_PLATFORM: gvars.log.warning( - "Platform override is set, using '%s' as platform " - "instead of detected platform '%s'", - settings.OVERRIDE_PLATFORM, - consts.PLATFORM, + f"Platform override is set, using '{settings.OVERRIDE_PLATFORM}' as platform " + f"instead of detected platform '{consts.PLATFORM}'" ) platform = settings.OVERRIDE_PLATFORM else: @@ -1315,7 +1304,7 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b if settings.DEBUG >= 4: meth_strs = ", ".join(m.__name__ for m in METHODS) - gvars.log.debug("%d methods available: %s", len(METHODS), meth_strs) + gvars.log.debug(f"{len(METHODS)} methods available: {meth_strs}") # Filter methods by the type of MAC we're looking for, such as "ip" # for remote host methods or "iface" for local interface methods. @@ -1328,16 +1317,13 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b ] # type: List[Type[Method]] if not type_methods: - _warn_critical("No valid methods matching MAC type '%s'" % method_type) + _warn_critical(f"No valid methods matching MAC type '{method_type}'") return False if settings.DEBUG >= 2: type_strs = ", ".join(tm.__name__ for tm in type_methods) gvars.log.debug( - "%d type-filtered methods for '%s': %s", - len(type_methods), - method_type, - type_strs, + f"{len(type_methods)} type-filtered methods for '{method_type}': {type_strs}" ) # Filter methods by the platform we're running on @@ -1349,8 +1335,9 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b # If there isn't a method for the current platform, # then fallback to the generic platform "other". warn_msg = ( - "No methods for platform '%s'! Your system may not be supported. " - "Falling back to platform 'other'." % platform + f"No methods for platform '{platform}'! " + "Your system may not be supported. " + "Falling back to platform 'other'." ) gvars.log.warning(warn_msg) warnings.warn(warn_msg, RuntimeWarning, stacklevel=2) @@ -1361,17 +1348,13 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b if settings.DEBUG >= 2: plat_strs = ", ".join(pm.__name__ for pm in platform_methods) gvars.log.debug( - "%d platform-filtered methods for '%s' (method_type='%s'): %s", - len(platform_methods), - platform, - method_type, - plat_strs, + f"{len(platform_methods)} platform-filtered methods for '{platform}' " + f"(method_type='{method_type}'): {plat_strs}" ) if not platform_methods: _warn_critical( - "No valid methods found for MAC type '%s' and platform '%s'" - % (method_type, platform) + f"No valid methods found for MAC type '{method_type}' and platform '{platform}'" ) return False @@ -1396,21 +1379,18 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b if not METHOD_CACHE[method_type]: METHOD_CACHE[method_type] = method_instance elif settings.DEBUG: - gvars.log.debug("Test failed for method '%s'", str(method_instance)) + gvars.log.debug(f"Test failed for method '{method_instance!s}'") if not tested_methods: _warn_critical( - "All %d '%s' methods failed to test!" % (len(filtered_methods), method_type) + f"All {len(filtered_methods)} '{method_type}' methods failed to test!" ) return False if settings.DEBUG >= 2: tested_strs = ", ".join(str(ts) for ts in tested_methods) gvars.log.debug( - "%d tested methods for '%s': %s", - len(tested_methods), - method_type, - tested_strs, + f"{len(tested_methods)} tested methods for '{method_type}': {tested_strs}" ) # Populate fallback cache with all the tested methods, minus the currently active method @@ -1428,21 +1408,19 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b "Current fallback cache: %s", str({k: str(v) for k, v in FALLBACK_CACHE.items()}), ) - gvars.log.debug("Finished initializing '%s' method cache", method_type) + gvars.log.debug(f"Finished initializing '{method_type}' method cache") return True def _remove_unusable(method: Method, method_type: str) -> Optional[Method]: if not FALLBACK_CACHE[method_type]: - gvars.log.warning("No fallback method for unusable method '%s'!", str(method)) + gvars.log.warning(f"No fallback method for unusable method '{method!s}'!") METHOD_CACHE[method_type] = None else: METHOD_CACHE[method_type] = FALLBACK_CACHE[method_type].pop(0) gvars.log.warning( - "Falling back to '%s' for unusable method '%s'", - str(METHOD_CACHE[method_type]), - str(method), + f"Falling back to '{METHOD_CACHE[method_type]!s}' for unusable method '{method!s}'" ) return METHOD_CACHE[method_type] @@ -1453,15 +1431,12 @@ def _attempt_method_get(method: Method, method_type: str, arg: str) -> Optional[ Attempt to use methods, and if they fail, fallback to the next method in the cache. """ if not METHOD_CACHE[method_type] and not FALLBACK_CACHE[method_type]: - _warn_critical("No usable methods found for MAC type '%s'" % method_type) + _warn_critical(f"No usable methods found for MAC type '{method_type}'") return None if settings.DEBUG: gvars.log.debug( - "Attempting get() (method='%s', method_type='%s', arg='%s')", - str(method), - method_type, - arg, + f"Attempting get() (method='{method!s}', method_type='{method_type}', arg='{arg}')" ) result = None @@ -1478,20 +1453,14 @@ def _attempt_method_get(method: Method, method_type: str, arg: str) -> Optional[ # CalledProcessError, inspect the return code, and set self.unusable = True if ex.returncode != 1: gvars.log.warning( - "Cached Method '%s' failed for '%s' lookup with process exit " - "code '%d' != 1, marking unusable. Exception: %s", - str(method), - method_type, - ex.returncode, - str(ex), + f"Cached Method '{method!s}' failed for '{method_type}' lookup with process exit " + f"code '{ex.returncode}' != 1, marking unusable. Exception: {ex}" ) method.unusable = True except Exception as ex: gvars.log.warning( - "Cached Method '%s' failed for '%s' lookup with unhandled exception: %s", - str(method), - method_type, - str(ex), + f"Cached Method '{method!s}' failed for '{method_type}' " + f"lookup with unhandled exception: {ex}" ) method.unusable = True @@ -1522,24 +1491,20 @@ def get_by_method( (those methods that have the attribute ``network_request`` set to ``True``) """ if not arg and method_type != "default_iface": - gvars.log.error( - "Empty arg for method '%s' (raw value: %s)", method_type, repr(arg) - ) + gvars.log.error(f"Empty arg for method '{method_type}' (raw value: {arg!r})") return None if settings.FORCE_METHOD: gvars.log.warning( - "Forcing method '%s' to be used for '%s' lookup (arg: '%s')", - settings.FORCE_METHOD, - method_type, - arg, + f"Forcing method '{settings.FORCE_METHOD}' to be used for " + f"'{method_type}' lookup (arg: '{arg}')" ) forced_method = get_method_by_name(settings.FORCE_METHOD) if not forced_method: gvars.log.error( - "Invalid settings.FORCE_METHOD method name '%s'", settings.FORCE_METHOD + f"Invalid FORCE_METHOD method name '{settings.FORCE_METHOD}'" ) return None @@ -1551,9 +1516,7 @@ def get_by_method( # Initialize the cache if it hasn't been already if not initialize_method_cache(method_type, network_request): gvars.log.error( - "Failed to initialize method cache for method '%s' (arg: '%s')", - method_type, - arg, + f"Failed to initialize method cache for method '{method_type}' (arg: '{arg}')" ) return None @@ -1561,9 +1524,8 @@ def get_by_method( if not method: gvars.log.error( - "Initialization failed for method '%s'. It may not be supported " - "on this platform or another issue occurred.", - method_type, + f"Initialization failed for method '{method_type}'. " + f"It may not be supported on this platform or another issue occurred." ) return None @@ -1574,7 +1536,7 @@ def get_by_method( # Log normal get() failures if debugging is enabled if settings.DEBUG and not result: - gvars.log.debug("Method '%s' failed for '%s' lookup", str(method), method_type) + gvars.log.debug(f"Method '{method!s}' failed for '{method_type}' lookup") return result @@ -1644,7 +1606,7 @@ def get_mac_address( # TODO: can this return a IPv6 address? If so, handle that! ip = socket.gethostbyname(hostname) except Exception as ex: - gvars.log.error("Could not resolve hostname '%s': %s", hostname, ex) + gvars.log.error(f"Could not resolve hostname '{hostname}': {ex}") if settings.DEBUG: gvars.log.debug(traceback.format_exc()) return None @@ -1657,7 +1619,7 @@ def get_mac_address( ) return None elif ":" not in ip6: - gvars.log.error("Invalid IPv6 address (no ':'): %s", ip6) + gvars.log.error(f"Invalid IPv6 address (no ':'): {ip6}") return None mac = None @@ -1707,9 +1669,8 @@ def get_mac_address( if send_udp_packet and not mac: if settings.DEBUG: gvars.log.debug( - "Attempting to populate ARP table with UDP packet to %s:%d", - ip if ip else ip6, - settings.PORT, + f"Attempting to populate ARP table with UDP packet " + f"to {ip if ip else ip6}:{settings.PORT}" ) if ip: @@ -1730,8 +1691,8 @@ def get_mac_address( sock.close() elif settings.DEBUG: gvars.log.debug( - "Not sending UDP packet, using network request method '%s' instead", - str(METHOD_CACHE["ip4"]), + f"Not sending UDP packet, using network request " + f"method '{METHOD_CACHE['ip4']!s}' instead" ) # Setup the address hunt based on the arguments specified @@ -1772,11 +1733,11 @@ def get_mac_address( if not mac: mac = get_by_method("iface", "lo") - gvars.log.debug("Raw MAC found: %s", mac) + gvars.log.debug(f"Raw MAC found: {mac}") # Log how long it took if settings.DEBUG: duration = timeit.default_timer() - start_time - gvars.log.debug("getmac took %.4f seconds", duration) + gvars.log.debug(f"getmac took {duration:0.4f} seconds") return _clean_mac(mac) From 90440f4ab355f5de482807a315ed646786014e0e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 13 Oct 2023 00:30:37 -0600 Subject: [PATCH 039/112] Reorganize CLI help for clarity --- docs/TODO.md | 2 +- getmac/__main__.py | 69 +++++++++++++++++++++++++--------------------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 68c2279..d13346c 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -44,7 +44,7 @@ ## Breaking changes (or potentially breaking) - [x] rewrite strings to f-strings -- [ ] CLI: put "override" and other debugging-related arguments into a separate argparse argument group +- [x] CLI: put "override" and other debugging-related arguments into a separate argparse argument group - [ ] Split getmac.py into separate files for methods, utils, etc. - [x] Refactor how global variables are handled - [ ] Remove all Python "Scripts" from the path, so they don't interfere with the commands we actually want (e.g. "ping"). Document this behavior! diff --git a/getmac/__main__.py b/getmac/__main__.py index dfad52c..8e97d5b 100644 --- a/getmac/__main__.py +++ b/getmac/__main__.py @@ -10,23 +10,31 @@ def main() -> None: parser = argparse.ArgumentParser( - "getmac", - description="Get the MAC address of system network " - "interfaces or remote hosts on the LAN", + prog="getmac", + description="Get MAC addresses of network interfaces or LAN hosts", ) parser.add_argument( "--version", action="version", version=f"getmac {getmac.__version__}" ) - parser.add_argument( - "-v", "--verbose", action="store_true", help="Enable output messages" + + group = parser.add_mutually_exclusive_group(required=False) + group.add_argument( + "-i", + "--interface", + type=str, + default=None, + help="Name of a network interface on the system", ) - parser.add_argument( - "-d", - "--debug", - action="count", - help="Enable debugging output. Add characters to " - "increase verbosity of output, e.g. '-dd'.", + group.add_argument( + "-4", "--ip", type=str, default=None, help="IPv4 address of a remote host" + ) + group.add_argument( + "-6", "--ip6", type=str, default=None, help="IPv6 address of a remote host" + ) + group.add_argument( + "-n", "--hostname", type=str, default=None, help="Hostname of a remote host" ) + parser.add_argument( "-N", "--no-net", @@ -35,14 +43,29 @@ def main() -> None: dest="NO_NET", help="Do not use arping or send a UDP packet to refresh the ARP table", ) - parser.add_argument( + + tshooting = parser.add_argument_group("troubleshooting") + tshooting.add_argument( + "-v", + "--verbose", + action="store_true", + help="Enable logging messages (by default, only the MAC is printed to the terminal)", + ) + tshooting.add_argument( + "-d", + "--debug", + action="count", + help="Enable debugging output. Add 'd' characters to " + "increase verbosity of output, e.g. '-dd' to set DEBUG=2.", + ) + tshooting.add_argument( "--override-port", type=int, metavar="PORT", help="Override the default UDP port used to refresh the ARP table " "if network requests are enabled and arping is unavailable", ) - parser.add_argument( + tshooting.add_argument( "--override-platform", type=str, default=None, @@ -51,7 +74,7 @@ def main() -> None: "(e.g. 'linux', 'windows', 'freebsd', etc.'). " "Any values returned by platform.system() are valid.", ) - parser.add_argument( + tshooting.add_argument( "--force-method", type=str, default=None, @@ -61,24 +84,6 @@ def main() -> None: "compatibility, and Method.test() will NOT be checked!", ) - group = parser.add_mutually_exclusive_group(required=False) - group.add_argument( - "-i", - "--interface", - type=str, - default=None, - help="Name of a network interface on the system", - ) - group.add_argument( - "-4", "--ip", type=str, default=None, help="IPv4 address of a remote host" - ) - group.add_argument( - "-6", "--ip6", type=str, default=None, help="IPv6 address of a remote host" - ) - group.add_argument( - "-n", "--hostname", type=str, default=None, help="Hostname of a remote host" - ) - args = parser.parse_args() if args.debug or args.verbose: From ea09e518c7b4bad11026442737d30ca0196a2e9b Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 13 Oct 2023 00:43:45 -0600 Subject: [PATCH 040/112] Remove Python "Scripts" from path on Windows --- docs/TODO.md | 9 +++++---- getmac/variables.py | 24 +++++++++++++++++------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index d13346c..a65ed83 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -43,14 +43,11 @@ ## Breaking changes (or potentially breaking) -- [x] rewrite strings to f-strings -- [x] CLI: put "override" and other debugging-related arguments into a separate argparse argument group - [ ] Split getmac.py into separate files for methods, utils, etc. -- [x] Refactor how global variables are handled -- [ ] Remove all Python "Scripts" from the path, so they don't interfere with the commands we actually want (e.g. "ping"). Document this behavior! - [ ] Replace the `UuidArpGetNode` method. It calls 3 commands and is quite inefficient, and doesn't exist in Python 3.9+. We should just take the methods and use directly. - [ ] **Consolidate `ip6` argument into `ip` argument.**. Parse based on `::` character vs `.` character if `str` or via `.version == 4`/`.version == 6` for `ipaddress` objects. - Combine `--ip` and `--ip6` CLI arguments into `--ip` output. this would make it *much* easier to test methods. + - keep `-4,`, `-6`, and `--ip6` arguments for backwards-compatibility until 1.1.0 - [ ] **API changes** (technically speaking) - Add argument to `get_mac_address()` to force the platform used (e.g. `platform_override="linux"`) - Also add CLI argument to configure this @@ -100,6 +97,10 @@ - [x] Support Python 3.10 and 3.11 - [x] Update pytest (pytest 4, which we were using to support python 2.7, doesn't work with python 3.10) - [x] add tests + setup.py classifier +- [x] Refactor how global variables are handled +- [x] rewrite strings to f-strings +- [x] CLI: put "override" and other debugging-related arguments into a separate argparse argument group +- [x] Remove all Python "Scripts" from the path, so they don't interfere with the commands we actually want (e.g. "ping"). # Etc diff --git a/getmac/variables.py b/getmac/variables.py index 0db466f..9d5d7d5 100644 --- a/getmac/variables.py +++ b/getmac/variables.py @@ -88,7 +88,6 @@ class Variables(VarsClass): #: Get and cache the configured system PATH on import #: The process environment does not change after a process is started - # TODO: move to Constants? PATH: List[str] = os.environ.get("PATH", os.defpath).split(os.pathsep) PATH_STR: str = os.pathsep.join(PATH) @@ -116,12 +115,23 @@ def __init__(self) -> None: if not Constants.WINDOWS: self.PATH.extend(("/sbin", "/usr/sbin")) else: - # TODO: Prevent edge case on Windows where our script "getmac.exe" - # gets added to the path ahead of the actual Windows getmac.exe - # This just handles case where it's in a virtualenv, won't work /w global scripts - self.PATH = [p for p in self.PATH if "\\getmac\\Scripts" not in p] - - # Build the str after modifications are made + # Prevent edge case on Windows where our script "getmac.exe" + # gets added to the path ahead of the actual Windows getmac.exe. + # This also prevents Python Scripts folders from being added, e.g. + # ...\\Python\\Python38\\Scripts. This prevents the aformentioned edge + # case, and also prevents stuff like a pip-installed "ping.exe" from + # being used instead of the Windows ping.exe. + new_path = [] + for path in self.PATH: + if "\\getmac\\Scripts" not in path and not ( + "\\Python" in path and "\\Scripts" in path + ): + new_path.append(path) + + self.PATH = new_path + + # Rebuild the combined PATH string after modifications are made + # This will be used with shutil.which() for PATH lookups self.PATH_STR = os.pathsep.join(self.PATH) From ca9a0c41b4e40ced3136ae3dcce24521da88009a Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 13 Oct 2023 00:46:53 -0600 Subject: [PATCH 041/112] remove validate-pyproject --- getmac/variables.py | 2 +- poetry.lock | 50 +-------------------------------------------- pyproject.toml | 1 - tox.ini | 1 - 4 files changed, 2 insertions(+), 52 deletions(-) diff --git a/getmac/variables.py b/getmac/variables.py index 9d5d7d5..8c5edd8 100644 --- a/getmac/variables.py +++ b/getmac/variables.py @@ -118,7 +118,7 @@ def __init__(self) -> None: # Prevent edge case on Windows where our script "getmac.exe" # gets added to the path ahead of the actual Windows getmac.exe. # This also prevents Python Scripts folders from being added, e.g. - # ...\\Python\\Python38\\Scripts. This prevents the aformentioned edge + # ...\\Python\\Python38\\Scripts. This prevents the aforementioned edge # case, and also prevents stuff like a pip-installed "ping.exe" from # being used instead of the Windows ping.exe. new_path = [] diff --git a/poetry.lock b/poetry.lock index 82200b9..2bcc2cb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -411,20 +411,6 @@ files = [ [package.extras] testing = ["pre-commit"] -[[package]] -name = "fastjsonschema" -version = "2.17.1" -description = "Fastest Python implementation of JSON schema" -optional = false -python-versions = "*" -files = [ - {file = "fastjsonschema-2.17.1-py3-none-any.whl", hash = "sha256:4b90b252628ca695280924d863fe37234eebadc29c5360d322571233dc9746e0"}, - {file = "fastjsonschema-2.17.1.tar.gz", hash = "sha256:f4eeb8a77cef54861dbf7424ac8ce71306f12cbb086c45131bcba2c6a4f726e3"}, -] - -[package.extras] -devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] - [[package]] name = "filelock" version = "3.12.0" @@ -1265,17 +1251,6 @@ tox = ">=4,<5" [package.extras] testing = ["black", "devpi-process", "flake8 (>=6,<7)", "mypy", "pytest (>=7,<8)", "pytest-cov (>=3,<4)", "pytest-mock (>=3,<4)", "pytest-randomly (>=3)"] -[[package]] -name = "trove-classifiers" -version = "2023.5.24" -description = "Canonical source for classifiers on PyPI (pypi.org)." -optional = false -python-versions = "*" -files = [ - {file = "trove-classifiers-2023.5.24.tar.gz", hash = "sha256:fd5a1546283be941f47540a135bdeae8fb261380a6a204d9c18012f2a1b0ceae"}, - {file = "trove_classifiers-2023.5.24-py3-none-any.whl", hash = "sha256:d9d7ae14fb90bf3d50bef99c3941b176b5326509e6e9037e622562d6352629d0"}, -] - [[package]] name = "typed-ast" version = "1.5.4" @@ -1337,29 +1312,6 @@ secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17. socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] -[[package]] -name = "validate-pyproject" -version = "0.13" -description = "Validation library and CLI tool for checking on 'pyproject.toml' files using JSON Schema" -optional = false -python-versions = ">=3.6" -files = [ - {file = "validate-pyproject-0.13.tar.gz", hash = "sha256:9fc9153ea587592a37ed5313628221bd276886497cb8ec35a2e02c28ddd39911"}, - {file = "validate_pyproject-0.13-py3-none-any.whl", hash = "sha256:5026aabfb376744032e5f9cdfceb4e75badce181b21398f64a768c373531b3e0"}, -] - -[package.dependencies] -fastjsonschema = ">=2.16.2,<=3" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -packaging = {version = ">=20.4", optional = true, markers = "extra == \"all\""} -tomli = {version = ">=1.2.1", optional = true, markers = "python_version < \"3.11\" and extra == \"all\""} -trove-classifiers = {version = ">=2021.10.20", optional = true, markers = "extra == \"all\""} - -[package.extras] -all = ["packaging (>=20.4)", "tomli (>=1.2.1)", "trove-classifiers (>=2021.10.20)"] -testing = ["pytest", "pytest-cov", "pytest-randomly", "pytest-xdist", "setuptools"] -typecheck = ["importlib-resources", "mypy"] - [[package]] name = "virtualenv" version = "20.23.0" @@ -1413,4 +1365,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "fee59ec718eacf6f18491eda80a087d7468950c45ce3d76121d24dcb2fc44981" +content-hash = "2d78e0dde61e1a281c3aca20bfe5e26071727ed3abcbb8658c50988c74e39ae9" diff --git a/pyproject.toml b/pyproject.toml index 77a6345..f8b1272 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -193,7 +193,6 @@ codespell = ">=2.2.4" tomli = { version = "*", python = "<3.12" } ruff = "*" vulture = "*" -validate-pyproject = {extras = ["all"], version = "^0.13"} black = "^23.3.0" mypy = "^1.3.0" diff --git a/tox.ini b/tox.ini index 04665dd..e018826 100644 --- a/tox.ini +++ b/tox.ini @@ -52,6 +52,5 @@ commands = poetry run codespell --toml {toxinidir}/pyproject.toml poetry run ruff check getmac tests poetry run vulture --min-confidence 100 getmac tests - poetry run validate-pyproject {toxinidir}/pyproject.toml poetry run black --check --quiet getmac tests poetry run mypy getmac tests From d17a508290d2ea4fe2cc72e8f864657585904102 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 13 Oct 2023 00:52:49 -0600 Subject: [PATCH 042/112] update dev dependencies --- getmac/getmac.py | 2 +- poetry.lock | 563 +++++++++++++++++++++++++---------------------- pyproject.toml | 3 +- 3 files changed, 302 insertions(+), 266 deletions(-) diff --git a/getmac/getmac.py b/getmac/getmac.py index 8b5b22a..82f8c3e 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -157,7 +157,7 @@ def _call_proc(executable: str, args: str) -> str: ) if settings.DEBUG >= 4: - gvars.log.debug(f"Output from '{executable}' command: {str(output)}") + gvars.log.debug(f"Output from '{executable}' command: {output!s}") if isinstance(output, bytes): output = output.decode("utf-8") diff --git a/poetry.lock b/poetry.lock index 2bcc2cb..68cabad 100644 --- a/poetry.lock +++ b/poetry.lock @@ -13,18 +13,21 @@ files = [ [[package]] name = "babel" -version = "2.12.1" +version = "2.13.0" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ - {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, - {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, + {file = "Babel-2.13.0-py3-none-any.whl", hash = "sha256:fbfcae1575ff78e26c7449136f1abbefc3c13ce542eeb13d43d50d8b047216ec"}, + {file = "Babel-2.13.0.tar.gz", hash = "sha256:04c3e2d28d2b7681644508f836be388ae49e0cfe91465095340395b60d00f210"}, ] [package.dependencies] pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + [[package]] name = "beautifulsoup4" version = "4.12.2" @@ -106,119 +109,134 @@ files = [ [[package]] name = "certifi" -version = "2023.5.7" +version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, - {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, ] [[package]] name = "chardet" -version = "5.1.0" +version = "5.2.0" description = "Universal encoding detector for Python 3" optional = false python-versions = ">=3.7" files = [ - {file = "chardet-5.1.0-py3-none-any.whl", hash = "sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"}, - {file = "chardet-5.1.0.tar.gz", hash = "sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5"}, + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, ] [[package]] name = "charset-normalizer" -version = "3.1.0" +version = "3.3.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, - {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, + {file = "charset-normalizer-3.3.0.tar.gz", hash = "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-win32.whl", hash = "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-win32.whl", hash = "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-win32.whl", hash = "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-win32.whl", hash = "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-win32.whl", hash = "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-win32.whl", hash = "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884"}, + {file = "charset_normalizer-3.3.0-py3-none-any.whl", hash = "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2"}, ] [[package]] name = "click" -version = "8.1.3" +version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] @@ -227,17 +245,17 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "codespell" -version = "2.2.4" +version = "2.2.5" description = "Codespell" optional = false python-versions = ">=3.7" files = [ - {file = "codespell-2.2.4-py3-none-any.whl", hash = "sha256:7d984b8130108e6f82524b7d09f8b7bf2fb1e398c5d4b37d9e2bd310145b3e29"}, - {file = "codespell-2.2.4.tar.gz", hash = "sha256:0b4620473c257d9cde1ff8998b26b2bb209a35c2b7489f5dc3436024298ce83a"}, + {file = "codespell-2.2.5-py3-none-any.whl", hash = "sha256:efa037f54b73c84f7bd14ce8e853d5f822cdd6386ef0ff32e957a3919435b9ec"}, + {file = "codespell-2.2.5.tar.gz", hash = "sha256:6d9faddf6eedb692bf80c9a94ec13ab4f5fb585aabae5f3750727148d7b5be56"}, ] [package.extras] -dev = ["Pygments", "build", "chardet", "flake8", "flake8-pyproject", "pytest", "pytest-cov", "pytest-dependency", "tomli"] +dev = ["Pygments", "build", "chardet", "pytest", "pytest-cov", "pytest-dependency", "ruff", "tomli"] hard-encoding-detection = ["chardet"] toml = ["tomli"] types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] @@ -353,13 +371,13 @@ yaml = ["PyYAML (>=3.10)"] [[package]] name = "distlib" -version = "0.3.6" +version = "0.3.7" description = "Distribution utilities" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, + {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, + {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, ] [[package]] @@ -385,13 +403,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.1.1" +version = "1.1.3" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, - {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, ] [package.extras] @@ -399,42 +417,42 @@ test = ["pytest (>=6)"] [[package]] name = "execnet" -version = "1.9.0" +version = "2.0.2" description = "execnet: rapid multi-Python deployment" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" files = [ - {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, - {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, + {file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"}, + {file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"}, ] [package.extras] -testing = ["pre-commit"] +testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "filelock" -version = "3.12.0" +version = "3.12.2" description = "A platform independent file lock." optional = false python-versions = ">=3.7" files = [ - {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, - {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, + {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, + {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, ] [package.extras] -docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] [[package]] name = "furo" -version = "2023.5.20" +version = "2023.9.10" description = "A clean customisable Sphinx documentation theme." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "furo-2023.5.20-py3-none-any.whl", hash = "sha256:594a8436ddfe0c071f3a9e9a209c314a219d8341f3f1af33fdf7c69544fab9e6"}, - {file = "furo-2023.5.20.tar.gz", hash = "sha256:40e09fa17c6f4b22419d122e933089226dcdb59747b5b6c79363089827dea16f"}, + {file = "furo-2023.9.10-py3-none-any.whl", hash = "sha256:513092538537dc5c596691da06e3c370714ec99bc438680edc1debffb73e5bfc"}, + {file = "furo-2023.9.10.tar.gz", hash = "sha256:5707530a476d2a63b8cad83b4f961f3739a69f4b058bcf38a03a39fa537195b2"}, ] [package.dependencies] @@ -467,13 +485,13 @@ files = [ [[package]] name = "importlib-metadata" -version = "6.6.0" +version = "6.7.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, - {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, + {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, + {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, ] [package.dependencies] @@ -483,7 +501,7 @@ zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] [[package]] name = "iniconfig" @@ -574,44 +592,44 @@ files = [ [[package]] name = "mypy" -version = "1.3.0" +version = "1.4.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.7" files = [ - {file = "mypy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eb485cea53f4f5284e5baf92902cd0088b24984f4209e25981cc359d64448d"}, - {file = "mypy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c99c3ecf223cf2952638da9cd82793d8f3c0c5fa8b6ae2b2d9ed1e1ff51ba85"}, - {file = "mypy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:550a8b3a19bb6589679a7c3c31f64312e7ff482a816c96e0cecec9ad3a7564dd"}, - {file = "mypy-1.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cbc07246253b9e3d7d74c9ff948cd0fd7a71afcc2b77c7f0a59c26e9395cb152"}, - {file = "mypy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:a22435632710a4fcf8acf86cbd0d69f68ac389a3892cb23fbad176d1cddaf228"}, - {file = "mypy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6e33bb8b2613614a33dff70565f4c803f889ebd2f859466e42b46e1df76018dd"}, - {file = "mypy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d23370d2a6b7a71dc65d1266f9a34e4cde9e8e21511322415db4b26f46f6b8c"}, - {file = "mypy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:658fe7b674769a0770d4b26cb4d6f005e88a442fe82446f020be8e5f5efb2fae"}, - {file = "mypy-1.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d29e324cdda61daaec2336c42512e59c7c375340bd202efa1fe0f7b8f8ca"}, - {file = "mypy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:d0b6c62206e04061e27009481cb0ec966f7d6172b5b936f3ead3d74f29fe3dcf"}, - {file = "mypy-1.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:76ec771e2342f1b558c36d49900dfe81d140361dd0d2df6cd71b3db1be155409"}, - {file = "mypy-1.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc95f8386314272bbc817026f8ce8f4f0d2ef7ae44f947c4664efac9adec929"}, - {file = "mypy-1.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:faff86aa10c1aa4a10e1a301de160f3d8fc8703b88c7e98de46b531ff1276a9a"}, - {file = "mypy-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:8c5979d0deb27e0f4479bee18ea0f83732a893e81b78e62e2dda3e7e518c92ee"}, - {file = "mypy-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c5d2cc54175bab47011b09688b418db71403aefad07cbcd62d44010543fc143f"}, - {file = "mypy-1.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:87df44954c31d86df96c8bd6e80dfcd773473e877ac6176a8e29898bfb3501cb"}, - {file = "mypy-1.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:473117e310febe632ddf10e745a355714e771ffe534f06db40702775056614c4"}, - {file = "mypy-1.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:74bc9b6e0e79808bf8678d7678b2ae3736ea72d56eede3820bd3849823e7f305"}, - {file = "mypy-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:44797d031a41516fcf5cbfa652265bb994e53e51994c1bd649ffcd0c3a7eccbf"}, - {file = "mypy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ddae0f39ca146972ff6bb4399f3b2943884a774b8771ea0a8f50e971f5ea5ba8"}, - {file = "mypy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c4c42c60a8103ead4c1c060ac3cdd3ff01e18fddce6f1016e08939647a0e703"}, - {file = "mypy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e86c2c6852f62f8f2b24cb7a613ebe8e0c7dc1402c61d36a609174f63e0ff017"}, - {file = "mypy-1.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f9dca1e257d4cc129517779226753dbefb4f2266c4eaad610fc15c6a7e14283e"}, - {file = "mypy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:95d8d31a7713510685b05fbb18d6ac287a56c8f6554d88c19e73f724a445448a"}, - {file = "mypy-1.3.0-py3-none-any.whl", hash = "sha256:a8763e72d5d9574d45ce5881962bc8e9046bf7b375b0abf031f3e6811732a897"}, - {file = "mypy-1.3.0.tar.gz", hash = "sha256:e1f4d16e296f5135624b34e8fb741eb0eadedca90862405b1f1fde2040b9bd11"}, + {file = "mypy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8"}, + {file = "mypy-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878"}, + {file = "mypy-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd"}, + {file = "mypy-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc"}, + {file = "mypy-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1"}, + {file = "mypy-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462"}, + {file = "mypy-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258"}, + {file = "mypy-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2"}, + {file = "mypy-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7"}, + {file = "mypy-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01"}, + {file = "mypy-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b"}, + {file = "mypy-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b"}, + {file = "mypy-1.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7"}, + {file = "mypy-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9"}, + {file = "mypy-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042"}, + {file = "mypy-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3"}, + {file = "mypy-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6"}, + {file = "mypy-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f"}, + {file = "mypy-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc"}, + {file = "mypy-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828"}, + {file = "mypy-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3"}, + {file = "mypy-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816"}, + {file = "mypy-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c"}, + {file = "mypy-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f"}, + {file = "mypy-1.4.1-py3-none-any.whl", hash = "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4"}, + {file = "mypy-1.4.1.tar.gz", hash = "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} -typing-extensions = ">=3.10" +typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] @@ -632,53 +650,53 @@ files = [ [[package]] name = "packaging" -version = "23.1" +version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] name = "pathspec" -version = "0.11.1" +version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.7" files = [ - {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, - {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, ] [[package]] name = "platformdirs" -version = "3.5.1" +version = "3.11.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, - {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, + {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, + {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, ] [package.dependencies] -typing-extensions = {version = ">=4.5", markers = "python_version < \"3.8\""} +typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""} [package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pluggy" -version = "1.0.0" +version = "1.2.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, + {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, + {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, ] [package.dependencies] @@ -727,13 +745,13 @@ files = [ [[package]] name = "pygments" -version = "2.15.1" +version = "2.16.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ - {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, - {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, + {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, + {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, ] [package.extras] @@ -741,32 +759,32 @@ plugins = ["importlib-metadata"] [[package]] name = "pyproject-api" -version = "1.5.1" +version = "1.5.3" description = "API to interact with the python pyproject.toml based projects" optional = false python-versions = ">=3.7" files = [ - {file = "pyproject_api-1.5.1-py3-none-any.whl", hash = "sha256:4698a3777c2e0f6b624f8a4599131e2a25376d90fe8d146d7ac74c67c6f97c43"}, - {file = "pyproject_api-1.5.1.tar.gz", hash = "sha256:435f46547a9ff22cf4208ee274fca3e2869aeb062a4834adfc99a4dd64af3cf9"}, + {file = "pyproject_api-1.5.3-py3-none-any.whl", hash = "sha256:14cf09828670c7b08842249c1f28c8ee6581b872e893f81b62d5465bec41502f"}, + {file = "pyproject_api-1.5.3.tar.gz", hash = "sha256:ffb5b2d7cad43f5b2688ab490de7c4d3f6f15e0b819cb588c4b771567c9729eb"}, ] [package.dependencies] -packaging = ">=23" +packaging = ">=23.1" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [package.extras] -docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] -testing = ["covdefaults (>=2.2.2)", "importlib-metadata (>=6)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "virtualenv (>=20.17.1)", "wheel (>=0.38.4)"] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "importlib-metadata (>=6.6)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "setuptools (>=67.8)", "wheel (>=0.40)"] [[package]] name = "pytest" -version = "7.3.1" +version = "7.4.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, - {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, + {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, + {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, ] [package.dependencies] @@ -779,7 +797,7 @@ pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-benchmark" @@ -821,13 +839,13 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-mock" -version = "3.10.0" +version = "3.11.1" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-mock-3.10.0.tar.gz", hash = "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f"}, - {file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"}, + {file = "pytest-mock-3.11.1.tar.gz", hash = "sha256:7f6b125602ac6d743e523ae0bfa71e1a697a2f5534064528c6ff84c2f7c2fc7f"}, + {file = "pytest_mock-3.11.1-py3-none-any.whl", hash = "sha256:21c279fff83d70763b05f8874cc9cfb3fcacd6d354247a976f9529d19f9acf39"}, ] [package.dependencies] @@ -873,13 +891,13 @@ testing = ["filelock"] [[package]] name = "pytz" -version = "2023.3" +version = "2023.3.post1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, - {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, + {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, + {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, ] [[package]] @@ -921,28 +939,28 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" -version = "0.0.270" +version = "0.0.292" description = "An extremely fast Python linter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.0.270-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:f74c4d550f7b8e808455ac77bbce38daafc458434815ba0bc21ae4bdb276509b"}, - {file = "ruff-0.0.270-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:643de865fd35cb76c4f0739aea5afe7b8e4d40d623df7e9e6ea99054e5cead0a"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca02e709b3308eb7255b5f74e779be23b5980fca3862eae28bb23069cd61ae4"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3ed3b198768d2b3a2300fb18f730cd39948a5cc36ba29ae9d4639a11040880be"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:739495d2dbde87cf4e3110c8d27bc20febf93112539a968a4e02c26f0deccd1d"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:08188f8351f4c0b6216e8463df0a76eb57894ca59a3da65e4ed205db980fd3ae"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0827b074635d37984fc98d99316bfab5c8b1231bb83e60dacc83bd92883eedb4"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d61ae4841313f6eeb8292dc349bef27b4ce426e62c36e80ceedc3824e408734"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0eb412f20e77529a01fb94d578b19dcb8331b56f93632aa0cce4a2ea27b7aeba"}, - {file = "ruff-0.0.270-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b775e2c5fc869359daf8c8b8aa0fd67240201ab2e8d536d14a0edf279af18786"}, - {file = "ruff-0.0.270-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:21f00e47ab2308617c44435c8dfd9e2e03897461c9e647ec942deb2a235b4cfd"}, - {file = "ruff-0.0.270-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0bbfbf6fd2436165566ca85f6e57be03ed2f0a994faf40180cfbb3604c9232ef"}, - {file = "ruff-0.0.270-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8af391ef81f7be960be10886a3c1aac0b298bde7cb9a86ec2b05faeb2081ce6b"}, - {file = "ruff-0.0.270-py3-none-win32.whl", hash = "sha256:b4c037fe2f75bcd9aed0c89c7c507cb7fa59abae2bd4c8b6fc331a28178655a4"}, - {file = "ruff-0.0.270-py3-none-win_amd64.whl", hash = "sha256:0012f9b7dc137ab7f1f0355e3c4ca49b562baf6c9fa1180948deeb6648c52957"}, - {file = "ruff-0.0.270-py3-none-win_arm64.whl", hash = "sha256:9613456b0b375766244c25045e353bc8890c856431cd97893c97b10cc93bd28d"}, - {file = "ruff-0.0.270.tar.gz", hash = "sha256:95db07b7850b30ebf32b27fe98bc39e0ab99db3985edbbf0754d399eb2f0e690"}, + {file = "ruff-0.0.292-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96"}, + {file = "ruff-0.0.292-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8"}, + {file = "ruff-0.0.292-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9"}, + {file = "ruff-0.0.292-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a"}, + {file = "ruff-0.0.292-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0"}, + {file = "ruff-0.0.292-py3-none-musllinux_1_2_i686.whl", hash = "sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0"}, + {file = "ruff-0.0.292-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016"}, + {file = "ruff-0.0.292-py3-none-win32.whl", hash = "sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003"}, + {file = "ruff-0.0.292-py3-none-win_amd64.whl", hash = "sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c"}, + {file = "ruff-0.0.292-py3-none-win_arm64.whl", hash = "sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68"}, + {file = "ruff-0.0.292.tar.gz", hash = "sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac"}, ] [[package]] @@ -958,13 +976,13 @@ files = [ [[package]] name = "soupsieve" -version = "2.4.1" +version = "2.5" description = "A modern CSS selector implementation for Beautiful Soup." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"}, - {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, ] [[package]] @@ -1023,13 +1041,13 @@ type-comment = ["typed-ast (>=1.5.4)"] [[package]] name = "sphinx-basic-ng" -version = "1.0.0b1" +version = "1.0.0b2" description = "A modern skeleton for Sphinx themes." optional = false python-versions = ">=3.7" files = [ - {file = "sphinx_basic_ng-1.0.0b1-py3-none-any.whl", hash = "sha256:ade597a3029c7865b24ad0eda88318766bcc2f9f4cef60df7e28126fde94db2a"}, - {file = "sphinx_basic_ng-1.0.0b1.tar.gz", hash = "sha256:89374bd3ccd9452a301786781e28c8718e99960f2d4f411845ea75fc7bb5a9b0"}, + {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, + {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, ] [package.dependencies] @@ -1207,42 +1225,42 @@ files = [ [[package]] name = "tox" -version = "4.5.2" +version = "4.8.0" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.7" files = [ - {file = "tox-4.5.2-py3-none-any.whl", hash = "sha256:f1a9541b292aa0449f6c7bb67dc0073f25f9086413c3922fe47f5168cbf7b2f4"}, - {file = "tox-4.5.2.tar.gz", hash = "sha256:ad87fb7a10ef476afb6eb7e408808057f42976ef0d30ad5fe023099ba493ce58"}, + {file = "tox-4.8.0-py3-none-any.whl", hash = "sha256:4991305a56983d750a0d848a34242be290452aa88d248f1bf976e4036ee8b213"}, + {file = "tox-4.8.0.tar.gz", hash = "sha256:2adacf435b12ccf10b9dfa9975d8ec0afd7cbae44d300463140d2117b968037b"}, ] [package.dependencies] -cachetools = ">=5.3" +cachetools = ">=5.3.1" chardet = ">=5.1" colorama = ">=0.4.6" -filelock = ">=3.12" -importlib-metadata = {version = ">=6.6", markers = "python_version < \"3.8\""} +filelock = ">=3.12.2" +importlib-metadata = {version = ">=6.7", markers = "python_version < \"3.8\""} packaging = ">=23.1" -platformdirs = ">=3.5.1" -pluggy = ">=1" -pyproject-api = ">=1.5.1" +platformdirs = ">=3.9.1" +pluggy = ">=1.2" +pyproject-api = ">=1.5.3" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.6.2", markers = "python_version < \"3.8\""} -virtualenv = ">=20.23" +typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""} +virtualenv = ">=20.24.1" [package.extras] -docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-argparse-cli (>=1.11)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "devpi-process (>=0.3)", "diff-cover (>=7.5)", "distlib (>=0.3.6)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.17)", "psutil (>=5.9.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-xdist (>=3.3.1)", "re-assert (>=1.1)", "time-machine (>=2.9)", "wheel (>=0.40)"] +docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.23.3,!=1.23.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.1.1)", "devpi-process (>=0.3.1)", "diff-cover (>=7.7)", "distlib (>=0.3.7)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.17.1)", "psutil (>=5.9.5)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-xdist (>=3.3.1)", "re-assert (>=1.1)", "time-machine (>=2.10)", "wheel (>=0.40)"] [[package]] name = "tox-gh-actions" -version = "3.1.1" +version = "3.1.3" description = "Seamless integration of tox into GitHub Actions" optional = false python-versions = ">=3.7" files = [ - {file = "tox-gh-actions-3.1.1.tar.gz", hash = "sha256:8092fbedef53d29ca4f5ee35e15b4324b2edcd5c9dc17db56777cddac23ac3c1"}, - {file = "tox_gh_actions-3.1.1-py2.py3-none-any.whl", hash = "sha256:06cbe70c80eb36c560df6e437c94a1782b894b1f7cc3afdd8cf85cdf62701bde"}, + {file = "tox-gh-actions-3.1.3.tar.gz", hash = "sha256:ffd4151fe8b62c6f401a2fc5a01317835d7ab380923f6e0d063c300750308328"}, + {file = "tox_gh_actions-3.1.3-py2.py3-none-any.whl", hash = "sha256:5954766fe2ed0e284f3cdc87535dfdf68d0f803f1011b17ff8cf52ed3156e6c1"}, ] [package.dependencies] @@ -1253,57 +1271,74 @@ testing = ["black", "devpi-process", "flake8 (>=6,<7)", "mypy", "pytest (>=7,<8) [[package]] name = "typed-ast" -version = "1.5.4" +version = "1.5.5" description = "a fork of Python 2 and 3 ast modules with type comment support" optional = false python-versions = ">=3.6" files = [ - {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, - {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, - {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, - {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, - {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, - {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, - {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, - {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, - {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, - {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, + {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, + {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, + {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, + {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, + {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, + {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, + {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, + {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, + {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, + {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, + {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, + {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, + {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, + {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, + {file = "typed_ast-1.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f214394fc1af23ca6d4e9e744804d890045d1643dd7e8229951e0ef39429b5"}, + {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:118c1ce46ce58fda78503eae14b7664163aa735b620b64b5b725453696f2a35c"}, + {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4919b808efa61101456e87f2d4c75b228f4e52618621c77f1ddcaae15904fa"}, + {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fc2b8c4e1bc5cd96c1a823a885e6b158f8451cf6f5530e1829390b4d27d0807f"}, + {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:16f7313e0a08c7de57f2998c85e2a69a642e97cb32f87eb65fbfe88381a5e44d"}, + {file = "typed_ast-1.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2b946ef8c04f77230489f75b4b5a4a6f24c078be4aed241cfabe9cbf4156e7e5"}, + {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, + {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, + {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, + {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, + {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, + {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, + {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, + {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, + {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, + {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, + {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, + {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, + {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, + {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, + {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, + {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, + {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, + {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, + {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, + {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, + {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, ] [[package]] name = "typing-extensions" -version = "4.6.3" +version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, - {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, ] [[package]] name = "urllib3" -version = "2.0.2" +version = "2.0.6" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" files = [ - {file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"}, - {file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"}, + {file = "urllib3-2.0.6-py3-none-any.whl", hash = "sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2"}, + {file = "urllib3-2.0.6.tar.gz", hash = "sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564"}, ] [package.extras] @@ -1314,34 +1349,34 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.23.0" +version = "20.24.5" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.23.0-py3-none-any.whl", hash = "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e"}, - {file = "virtualenv-20.23.0.tar.gz", hash = "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924"}, + {file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"}, + {file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"}, ] [package.dependencies] -distlib = ">=0.3.6,<1" -filelock = ">=3.11,<4" -importlib-metadata = {version = ">=6.4.1", markers = "python_version < \"3.8\""} -platformdirs = ">=3.2,<4" +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +importlib-metadata = {version = ">=6.6", markers = "python_version < \"3.8\""} +platformdirs = ">=3.9.1,<4" [package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.7.1)", "time-machine (>=2.9)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "vulture" -version = "2.7" +version = "2.9.1" description = "Find dead code" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "vulture-2.7-py2.py3-none-any.whl", hash = "sha256:bccc51064ed76db15a6b58277cea8885936af047f53d2655fb5de575e93d0bca"}, - {file = "vulture-2.7.tar.gz", hash = "sha256:67fb80a014ed9fdb599dd44bb96cb54311032a104106fc2e706ef7a6dad88032"}, + {file = "vulture-2.9.1-py2.py3-none-any.whl", hash = "sha256:a46857014619bd4b785a506b9ddb738cd4621043558309b03a879d18f86e2d72"}, + {file = "vulture-2.9.1.tar.gz", hash = "sha256:b6a2aa632b6fd51488a8eeac650ab4a509bb1a032e81943817a8a2e6a63a30b3"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index f8b1272..d5f704b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -295,7 +295,8 @@ ignore = [ "G004", # Logging statement uses f-string "PIE790", # no-unnecessary-pass "PGH003", # Use specific rule codes when ignoring type issues - "PLC1901", # compare-to-empty-string + # "PLC1901", # compare-to-empty-string + "RUF012", # Mutable class attributes should be annotated with typing.ClassVar ] [tool.ruff.per-file-ignores] From 72884a7a3c4522e0a8db11a9cc0ce34fb896992c Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 13 Oct 2023 00:53:23 -0600 Subject: [PATCH 043/112] add python 3.12 to CI --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 973d222..d8087a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,7 @@ jobs: - "3.9" - "3.10" - "3.11" + - "3.12" architecture: - x86 - x64 From 75ff84040d95c28a7d144573ccdb401d62b1451e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 17 Jul 2024 21:24:44 -0600 Subject: [PATCH 044/112] fix references --- getmac/getmac.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/getmac/getmac.py b/getmac/getmac.py index 7dacc91..cbd4972 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -349,7 +349,7 @@ class ArpVariousArgs(Method): ) _args_tested: bool = False _good_pair: Union[Tuple, Tuple[str, bool]] = () - _good_regex: str = _regex_darwin if DARWIN else _regex_std + _good_regex: str = _regex_darwin if consts.DARWIN else _regex_std def test(self) -> bool: return check_command("arp") @@ -398,7 +398,7 @@ def get(self, arg: str) -> Optional[str]: escaped = re.escape(arg) _good_regex = ( - self._regex_darwin if DARWIN or SOLARIS else self._regex_std + self._regex_darwin if consts.DARWIN or consts.SOLARIS else self._regex_std ) # type: str return _search(r"\(" + escaped + _good_regex, command_output) From ac38cd1acfd1895888b9e533f142a24d46da7c27 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 17 Jul 2024 22:02:23 -0600 Subject: [PATCH 045/112] Move utils into separate file This makes getmac.py more focused on the core functionality and easier to read (and ditto for the tests). --- getmac/getmac.py | 296 +++++++++++------------------------------- getmac/utils.py | 158 ++++++++++++++++++++++ tests/test_getmac.py | 89 ++----------- tests/test_methods.py | 136 +++++++++---------- tests/test_utils.py | 66 ++++++++++ 5 files changed, 384 insertions(+), 361 deletions(-) create mode 100644 getmac/utils.py create mode 100644 tests/test_utils.py diff --git a/getmac/getmac.py b/getmac/getmac.py index cbd4972..5cadfd2 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -24,164 +24,20 @@ import ctypes import os import re -import shlex import socket import struct import traceback import warnings -from shutil import which -from subprocess import DEVNULL, CalledProcessError, check_output +from subprocess import CalledProcessError from typing import Dict, List, Optional, Set, Tuple, Type, Union +from . import utils from .variables import settings, consts, gvars #: Current version of getmac package __version__ = "1.0.0a0" -def check_command(command: str) -> bool: - """ - Check if a command exists using `shutil.which()`. The result of the check - is cached in a global dict to speed up subsequent lookups. - """ - if command not in gvars.CHECK_COMMAND_CACHE: - gvars.CHECK_COMMAND_CACHE[command] = bool(which(command, path=gvars.PATH_STR)) - return gvars.CHECK_COMMAND_CACHE[command] - - -def check_path(filepath: str) -> bool: - """ - Check if the file pointed to by `filepath` exists and is readable. - """ - return os.path.exists(filepath) and os.access(filepath, os.R_OK) - - -def _clean_mac(mac: Optional[str]) -> Optional[str]: - """ - Check and format a string result to be lowercase colon-separated MAC. - """ - if mac is None: - return None - - # Handle cases where it's bytes - if isinstance(mac, bytes): - mac = mac.decode("utf-8") - - # Strip bad characters - for garbage_string in ["\\n", "\\r"]: - mac = mac.replace(garbage_string, "") - - # Remove trailing whitespace, make lowercase, remove spaces, - # and replace dashes '-' with colons ':'. - mac = mac.strip().lower().replace(" ", "").replace("-", ":") - - # Fix cases where there are no colons - if ":" not in mac and len(mac) == 12: - gvars.log.debug(f"Adding colons to MAC {mac}") - mac = ":".join(mac[i : i + 2] for i in range(0, len(mac), 2)) - - # Pad single-character octets with a leading zero (e.g. Darwin's ARP output) - elif len(mac) < 17: - gvars.log.debug( - f"Length of MAC {mac} is {len(mac)}, padding single-character octets with zeros" - ) - parts = mac.split(":") - new_mac = [] - for part in parts: - if len(part) == 1: - new_mac.append("0" + part) - else: - new_mac.append(part) - mac = ":".join(new_mac) - - # MAC address should ALWAYS be 17 characters before being returned - if len(mac) != 17: - gvars.log.warning(f"MAC address {mac} is not 17 characters long!") - mac = None - elif mac.count(":") != 5: - gvars.log.warning(f"MAC address {mac} is missing colon (':') characters") - mac = None - return mac - - -def _read_file(filepath: str) -> Optional[str]: - try: - with open(filepath) as f: - return f.read() - except OSError: - gvars.log.debug(f"Could not find file: '{filepath}'") - return None - - -def _search( - regex: str, text: str, group_index: int = 0, flags: int = 0 -) -> Optional[str]: - if not text: - if settings.DEBUG: - gvars.log.debug("No text to _search()") - return None - - match = re.search(regex, text, flags) - if match: - return match.groups()[group_index] - - return None - - -def _popen(command: str, args: str) -> str: - for directory in gvars.PATH: - executable = os.path.join(directory, command) - if ( - os.path.exists(executable) - and os.access(executable, os.F_OK | os.X_OK) - and not os.path.isdir(executable) - ): - break - else: - executable = command - - if settings.DEBUG >= 3: - gvars.log.debug(f"Running: '{executable} {args}'") - - return _call_proc(executable, args) - - -def _call_proc(executable: str, args: str) -> str: - if consts.WINDOWS: - cmd = executable + " " + args # type: ignore - else: - cmd = [executable, *shlex.split(args)] # type: ignore - - output: Union[str, bytes] = check_output( - cmd, stderr=DEVNULL, env=gvars.ENV # noqa: S603 - ) - - if settings.DEBUG >= 4: - gvars.log.debug(f"Output from '{executable}' command: {output!s}") - - if isinstance(output, bytes): - output = output.decode("utf-8") - - return output - - -def _uuid_convert(mac: int) -> str: - return ":".join(("%012X" % mac)[i : i + 2] for i in range(0, 12, 2)) - - -def _fetch_ip_using_dns() -> str: - """ - Determines the IP address of the default network interface. - - Sends a UDP packet to Cloudflare's DNS (``1.1.1.1``), which should go through - the default interface. This populates the source address of the socket, - which we then inspect and return. - """ - with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: - s.connect(("1.1.1.1", 53)) - return s.getsockname()[0] - - class Method: #: Valid platform identifier strings VALID_PLATFORM_NAMES: Set[str] = { @@ -264,9 +120,9 @@ def get(self, arg: str) -> Optional[str]: socket.gethostbyname = lambda x: arg # noqa: ARG005 mac1 = _arp_getnode() if mac1 is not None: - mac1 = _uuid_convert(mac1) + mac1 = utils.uuid_convert(mac1) mac2 = _arp_getnode() - mac2 = _uuid_convert(mac2) + mac2 = utils.uuid_convert(mac2) if mac1 == mac2: return mac1 except Exception: @@ -283,13 +139,13 @@ class ArpFile(Method): _path: str = os.environ.get("ARP_PATH", "/proc/net/arp") def test(self) -> bool: - return check_path(self._path) + return utils.check_path(self._path) def get(self, arg: str) -> Optional[str]: if not arg: return None - data = _read_file(self._path) + data = utils.read_file(self._path) if data is None: self.unusable = True @@ -304,7 +160,7 @@ def get(self, arg: str) -> Optional[str]: # Need a space, otherwise a search for 192.168.16.2 # will match 192.168.16.254 if it comes first! - return _search(re.escape(arg) + r" .+" + consts.MAC_RE_COLON, data) + return utils.search(re.escape(arg) + r" .+" + consts.MAC_RE_COLON, data) return None @@ -314,11 +170,11 @@ class ArpFreebsd(Method): method_type = "ip" def test(self) -> bool: - return check_command("arp") + return utils.check_command("arp") def get(self, arg: str) -> Optional[str]: regex = r"\(" + re.escape(arg) + r"\)\s+at\s+" + consts.MAC_RE_COLON - return _search(regex, _popen("arp", arg)) + return utils.search(regex, utils.popen("arp", arg)) class ArpOpenbsd(Method): @@ -327,10 +183,10 @@ class ArpOpenbsd(Method): _regex: str = r"[ ]+" + consts.MAC_RE_COLON def test(self) -> bool: - return check_command("arp") + return utils.check_command("arp") def get(self, arg: str) -> Optional[str]: - return _search(re.escape(arg) + self._regex, _popen("arp", "-an")) + return utils.search(re.escape(arg) + self._regex, utils.popen("arp", "-an")) class ArpVariousArgs(Method): @@ -352,7 +208,7 @@ class ArpVariousArgs(Method): _good_regex: str = _regex_darwin if consts.DARWIN else _regex_std def test(self) -> bool: - return check_command("arp") + return utils.check_command("arp") def get(self, arg: str) -> Optional[str]: if not arg: @@ -372,7 +228,7 @@ def get(self, arg: str) -> Optional[str]: if pair_to_test[1]: cmd_args.append(arg) - command_output = _popen("arp", " ".join(cmd_args)) + command_output = utils.popen("arp", " ".join(cmd_args)) self._good_pair = pair_to_test break except CalledProcessError as ex: @@ -394,13 +250,13 @@ def get(self, arg: str) -> Optional[str]: if self._good_pair[1]: cmd_args.append(arg) - command_output = _popen("arp", " ".join(cmd_args)) + command_output = utils.popen("arp", " ".join(cmd_args)) escaped = re.escape(arg) _good_regex = ( self._regex_darwin if consts.DARWIN or consts.SOLARIS else self._regex_std ) # type: str - return _search(r"\(" + escaped + _good_regex, command_output) + return utils.search(r"\(" + escaped + _good_regex, command_output) class ArpExe(Method): @@ -418,10 +274,10 @@ def test(self) -> bool: # NOTE: specifying "arp.exe" instead of "arp" lets this work # seamlessly on WSL1 as well. On WSL2 it doesn't matter, since # it's basically just a Linux VM with some lipstick. - return check_command("arp.exe") + return utils.check_command("arp.exe") def get(self, arg: str) -> Optional[str]: - return _search(consts.MAC_RE_DASH, _popen("arp.exe", f"-a {arg}")) + return utils.search(consts.MAC_RE_DASH, utils.popen("arp.exe", f"-a {arg}")) class ArpingHost(Method): @@ -459,7 +315,7 @@ class ArpingHost(Method): _iputils_args: str = "-f -c 1" def test(self) -> bool: - return check_command("arping") + return utils.check_command("arping") def get(self, arg: str) -> Optional[str]: # If busybox or iputils, this will just work, and if host ping fails, @@ -471,9 +327,9 @@ def get(self, arg: str) -> Optional[str]: # then re-try with Habets args. try: if self._is_iputils: - command_output = _popen("arping", f"{self._iputils_args} {arg}") + command_output = utils.popen("arping", f"{self._iputils_args} {arg}") if command_output: - return _search( + return utils.search( r" from %s \[(%s)\]" % (re.escape(arg), consts.MAC_RE_COLON), command_output, ) @@ -496,7 +352,7 @@ def get(self, arg: str) -> Optional[str]: return None def _call_habets(self, arg: str) -> Optional[str]: - command_output = _popen("arping", f"{self._habets_args} {arg}") + command_output = utils.popen("arping", f"{self._habets_args} {arg}") if command_output: return command_output.strip() else: @@ -559,10 +415,10 @@ class IpNeighborShow(Method): method_type = "ip" # IPv6 and IPv4 def test(self) -> bool: - return check_command("ip") + return utils.check_command("ip") def get(self, arg: str) -> Optional[str]: - output = _popen("ip", f"neighbor show {arg}") + output = utils.popen("ip", f"neighbor show {arg}") if not output: return None @@ -583,10 +439,10 @@ class SysIfaceFile(Method): def test(self) -> bool: # Imperfect, but should work well enough - return check_path(self._path) + return utils.check_path(self._path) def get(self, arg: str) -> Optional[str]: - data = _read_file(self._path + arg + "/address") + data = utils.read_file(self._path + arg + "/address") # NOTE: if "/sys/class/net/" exists, but interface file doesn't, # then that means the interface doesn't exist @@ -602,7 +458,7 @@ def test(self) -> bool: try: from uuid import _find_mac # type: ignore # noqa: F401 - return check_command("lanscan") + return utils.check_command("lanscan") except Exception: return False @@ -612,7 +468,7 @@ def get(self, arg: str) -> Optional[str]: mac = _find_mac("lanscan", "-ai", [arg.encode()], lambda i: 0) # noqa: ARG005 if mac: - return _uuid_convert(mac) + return utils.uuid_convert(mac) return None @@ -665,13 +521,13 @@ def test(self) -> bool: # NOTE: the scripts from this library (getmac) are excluded from the # path used for checking variables, in getmac.getmac.PATH (defined # at the top of this file). Otherwise, this would get messy quickly :) - return check_command("getmac.exe") + return utils.check_command("getmac.exe") def get(self, arg: str) -> Optional[str]: try: # /nh: Suppresses table headers # /v: Verbose - command_output = _popen("getmac.exe", "/NH /V") + command_output = utils.popen("getmac.exe", "/NH /V") except CalledProcessError as ex: # This shouldn't cause an exception if it's valid command gvars.log.error(f"getmac.exe failed, marking unusable. Exception: {ex}") @@ -679,10 +535,10 @@ def get(self, arg: str) -> Optional[str]: return None if self._champ: - return _search(self._champ[0] + arg + self._champ[1], command_output) + return utils.search(self._champ[0] + arg + self._champ[1], command_output) for pair in self._regexes: - result = _search(pair[0] + arg + pair[1], command_output) + result = utils.search(pair[0] + arg + pair[1], command_output) if result: self._champ = pair return result @@ -707,10 +563,10 @@ class IpconfigExe(Method): ) def test(self) -> bool: - return check_command("ipconfig.exe") + return utils.check_command("ipconfig.exe") def get(self, arg: str) -> Optional[str]: - return _search(arg + self._regex, _popen("ipconfig.exe", "/all")) + return utils.search(arg + self._regex, utils.popen("ipconfig.exe", "/all")) class WmicExe(Method): @@ -729,10 +585,10 @@ class WmicExe(Method): method_type = "iface" def test(self) -> bool: - return check_command("wmic.exe") + return utils.check_command("wmic.exe") def get(self, arg: str) -> Optional[str]: - command_output = _popen( + command_output = utils.popen( "wmic.exe", f'nic where "NetConnectionID = \'{arg}\'" get "MACAddress" /value', ) @@ -762,11 +618,11 @@ class DarwinNetworksetupIface(Method): method_type = "iface" def test(self) -> bool: - return check_command("networksetup") + return utils.check_command("networksetup") def get(self, arg: str) -> Optional[str]: - command_output = _popen("networksetup", f"-getmacaddress {arg}") - return _search(consts.MAC_RE_COLON, command_output) + command_output = utils.popen("networksetup", f"-getmacaddress {arg}") + return utils.search(consts.MAC_RE_COLON, command_output) # This only took 15-20 hours of throwing my brain against a wall multiple times @@ -792,7 +648,7 @@ def _parse_ifconfig(iface: str, command_output: str) -> Optional[str]: # "(?:^|\s)": prevent an input of "h0" from matching on "eth0" search_re = r"(?:^|\s)" + iface + IFCONFIG_REGEX - return _search(search_re, command_output, flags=re.DOTALL) + return utils.search(search_re, command_output, flags=re.DOTALL) class IfconfigWithIfaceArg(Method): @@ -805,11 +661,11 @@ class IfconfigWithIfaceArg(Method): method_type = "iface" def test(self) -> bool: - return check_command("ifconfig") + return utils.check_command("ifconfig") def get(self, arg: str) -> Optional[str]: try: - command_output = _popen("ifconfig", arg) + command_output = utils.popen("ifconfig", arg) except CalledProcessError as err: # Return code of 1 means interface doesn't exist if err.returncode == 1: @@ -829,7 +685,7 @@ class IfconfigEther(Method): _iface_arg: bool = False def test(self) -> bool: - return check_command("ifconfig") + return utils.check_command("ifconfig") def get(self, arg: str) -> Optional[str]: # Check if this version of "ifconfig" accepts an interface argument @@ -837,16 +693,16 @@ def get(self, arg: str) -> Optional[str]: if not self._tested_arg: try: - command_output = _popen("ifconfig", arg) + command_output = utils.popen("ifconfig", arg) self._iface_arg = True except CalledProcessError: self._iface_arg = False self._tested_arg = True if self._iface_arg and not command_output: # Don't repeat work on first run - command_output = _popen("ifconfig", arg) + command_output = utils.popen("ifconfig", arg) else: - command_output = _popen("ifconfig", "") + command_output = utils.popen("ifconfig", "") return _parse_ifconfig(arg, command_output) @@ -871,7 +727,7 @@ class IfconfigOther(Method): _good_pair: List[Union[str, Tuple[str, str]]] = [] def test(self) -> bool: - return check_command("ifconfig") + return utils.check_command("ifconfig") def get(self, arg: str) -> Optional[str]: if not arg: @@ -884,7 +740,7 @@ def get(self, arg: str) -> Optional[str]: if not self._args_tested: for pair_to_test in self._args: try: - command_output = _popen("ifconfig", pair_to_test[0]) + command_output = utils.popen("ifconfig", pair_to_test[0]) self._good_pair = list(pair_to_test) # type: ignore if isinstance(self._good_pair[1], str): self._good_pair[1] += consts.MAC_RE_COLON @@ -903,13 +759,13 @@ def get(self, arg: str) -> Optional[str]: self._args_tested = True if not command_output and isinstance(self._good_pair[0], str): - command_output = _popen("ifconfig", self._good_pair[0]) + command_output = utils.popen("ifconfig", self._good_pair[0]) # Handle the two possible search terms if isinstance(self._good_pair[1], tuple): for term in self._good_pair[1]: regex = term + consts.MAC_RE_COLON - result = _search(re.escape(arg) + regex, command_output) + result = utils.search(re.escape(arg) + regex, command_output) if result: # changes type from tuple to str, so the else statement @@ -918,7 +774,7 @@ def get(self, arg: str) -> Optional[str]: return result return None else: - return _search(re.escape(arg) + self._good_pair[1], command_output) + return utils.search(re.escape(arg) + self._good_pair[1], command_output) class NetstatIface(Method): @@ -936,13 +792,13 @@ class NetstatIface(Method): _working_regex: str = "" def test(self) -> bool: - return check_command("netstat") + return utils.check_command("netstat") # TODO: consolidate the parsing logic between IfconfigOther and netstat def get(self, arg: str) -> Optional[str]: # NOTE: netstat and ifconfig pull from the same kernel source and # therefore have the same output format on the same platform. - command_output = _popen("netstat", "-iae") + command_output = utils.popen("netstat", "-iae") if not command_output: gvars.log.warning("no netstat output, marking unusable") self.unusable = True @@ -951,11 +807,13 @@ def get(self, arg: str) -> Optional[str]: if self._working_regex: # Use regex that worked previously. This can still return None in # the case of interface not existing, but at least it's a bit faster. - return _search(arg + self._working_regex, command_output, flags=re.DOTALL) + return utils.search( + arg + self._working_regex, command_output, flags=re.DOTALL + ) # See if either regex matches for regex in self._regexes: - result = _search(arg + regex, command_output, flags=re.DOTALL) + result = utils.search(arg + regex, command_output, flags=re.DOTALL) if result: self._working_regex = regex return result @@ -985,7 +843,7 @@ class IpLinkIface(Method): _iface_arg: bool = False def test(self) -> bool: - return check_command("ip") + return utils.check_command("ip") def get(self, arg: str) -> Optional[str]: # Check if this version of "ip link" accepts an interface argument @@ -995,7 +853,7 @@ def get(self, arg: str) -> Optional[str]: if not self._tested_arg: try: - command_output = _popen("ip", "link show " + arg) + command_output = utils.popen("ip", "link show " + arg) self._iface_arg = True except CalledProcessError as err: # Output: 'Command "eth0" is unknown, try "ip link help"' @@ -1005,12 +863,12 @@ def get(self, arg: str) -> Optional[str]: if self._iface_arg: if not command_output: # Don't repeat work on first run - command_output = _popen("ip", "link show " + arg) - return _search(arg + self._regex, command_output) + command_output = utils.popen("ip", "link show " + arg) + return utils.search(arg + self._regex, command_output) else: # TODO: improve this regex to not need extra portion for no arg - command_output = _popen("ip", "link") - return _search(arg + r":" + self._regex, command_output) + command_output = utils.popen("ip", "link") + return utils.search(arg + r":" + self._regex, command_output) class DefaultIfaceLinuxRouteFile(Method): @@ -1027,10 +885,10 @@ class DefaultIfaceLinuxRouteFile(Method): method_type = "default_iface" def test(self) -> bool: - return check_path("/proc/net/route") + return utils.check_path("/proc/net/route") def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 - data = _read_file("/proc/net/route") + data = utils.read_file("/proc/net/route") if data is not None and len(data) > 1: for line in data.split("\n")[1:-1]: @@ -1065,10 +923,10 @@ class DefaultIfaceRouteCommand(Method): method_type = "default_iface" def test(self) -> bool: - return check_command("route") + return utils.check_command("route") def get(self, arg: str = "") -> Optional[str]: - output = _popen("route", "-n") + output = utils.popen("route", "-n") try: return ( @@ -1086,10 +944,10 @@ class DefaultIfaceRouteGetCommand(Method): method_type = "default_iface" def test(self) -> bool: - return check_command("route") + return utils.check_command("route") def get(self, arg: str = "") -> Optional[str]: - output = _popen("route", "get default") + output = utils.popen("route", "get default") if not output: return None @@ -1108,10 +966,10 @@ class DefaultIfaceIpRoute(Method): method_type = "default_iface" def test(self) -> bool: - return check_command("ip") + return utils.check_command("ip") def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 - output = _popen("ip", "route list 0/0") + output = utils.popen("ip", "route list 0/0") if not output: if settings.DEBUG: @@ -1126,10 +984,10 @@ class DefaultIfaceOpenBsd(Method): method_type = "default_iface" def test(self) -> bool: - return check_command("route") + return utils.check_command("route") def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 - output = _popen("route", "-nq show -inet -gateway -priority 1") + output = utils.popen("route", "-nq show -inet -gateway -priority 1") return output.partition("127.0.0.1")[0].strip().rpartition(" ")[2] @@ -1138,11 +996,11 @@ class DefaultIfaceFreeBsd(Method): method_type = "default_iface" def test(self) -> bool: - return check_command("netstat") + return utils.check_command("netstat") def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 - output = _popen("netstat", "-r") - return _search(r"default[ ]+\S+[ ]+\S+[ ]+(\S+)[\r\n]+", output) + output = utils.popen("netstat", "-r") + return utils.search(r"default[ ]+\S+[ ]+\S+[ ]+(\S+)[\r\n]+", output) # TODO: order methods by effectiveness/reliability @@ -1694,7 +1552,7 @@ def get_mac_address( else: # Default to searching for interface # Default to finding MAC of the interface with the default route if consts.WINDOWS and network_request: - default_iface_ip = _fetch_ip_using_dns() + default_iface_ip = utils.fetch_ip_using_dns() mac = get_by_method("ip4", default_iface_ip) elif consts.WINDOWS: # TODO: implement proper default interface detection on windows @@ -1728,4 +1586,4 @@ def get_mac_address( duration = timeit.default_timer() - start_time gvars.log.debug(f"getmac took {duration:0.4f} seconds") - return _clean_mac(mac) + return utils.clean_mac(mac) diff --git a/getmac/utils.py b/getmac/utils.py new file mode 100644 index 0000000..ad3db42 --- /dev/null +++ b/getmac/utils.py @@ -0,0 +1,158 @@ +""" +Utility and helper functions. These are basic in functionality +and should be relatively standalone. They are intended for +internal use by getmac. +""" + +import os +import re +import shlex +import socket +from shutil import which +from subprocess import DEVNULL, check_output +from typing import Optional, Union + +from .variables import settings, consts, gvars + + +def check_command(command: str) -> bool: + """ + Check if a command exists using `shutil.which()`. The result of the check + is cached in a global dict to speed up subsequent lookups. + """ + if command not in gvars.CHECK_COMMAND_CACHE: + gvars.CHECK_COMMAND_CACHE[command] = bool(which(command, path=gvars.PATH_STR)) + return gvars.CHECK_COMMAND_CACHE[command] + + +def check_path(filepath: str) -> bool: + """ + Check if the file pointed to by `filepath` exists and is readable. + """ + return os.path.exists(filepath) and os.access(filepath, os.R_OK) + + +def clean_mac(mac: Optional[str]) -> Optional[str]: + """ + Check and format a string result to be lowercase colon-separated MAC. + """ + if mac is None: + return None + + # Handle cases where it's bytes + if isinstance(mac, bytes): + mac = mac.decode("utf-8") + + # Strip bad characters + for garbage_string in ["\\n", "\\r"]: + mac = mac.replace(garbage_string, "") + + # Remove trailing whitespace, make lowercase, remove spaces, + # and replace dashes '-' with colons ':'. + mac = mac.strip().lower().replace(" ", "").replace("-", ":") + + # Fix cases where there are no colons + if ":" not in mac and len(mac) == 12: + gvars.log.debug(f"Adding colons to MAC {mac}") + mac = ":".join(mac[i : i + 2] for i in range(0, len(mac), 2)) + + # Pad single-character octets with a leading zero (e.g. Darwin's ARP output) + elif len(mac) < 17: + gvars.log.debug( + f"Length of MAC {mac} is {len(mac)}, padding single-character octets with zeros" + ) + parts = mac.split(":") + new_mac = [] + for part in parts: + if len(part) == 1: + new_mac.append("0" + part) + else: + new_mac.append(part) + mac = ":".join(new_mac) + + # MAC address should ALWAYS be 17 characters before being returned + if len(mac) != 17: + gvars.log.warning(f"MAC address {mac} is not 17 characters long!") + mac = None + elif mac.count(":") != 5: + gvars.log.warning(f"MAC address {mac} is missing colon (':') characters") + mac = None + return mac + + +def read_file(filepath: str) -> Optional[str]: + try: + with open(filepath) as f: + return f.read() + except OSError: + gvars.log.debug(f"Could not find file: '{filepath}'") + return None + + +def search( + regex: str, text: str, group_index: int = 0, flags: int = 0 +) -> Optional[str]: + if not text: + if settings.DEBUG: + gvars.log.debug("No text to _search()") + return None + + match = re.search(regex, text, flags) + if match: + return match.groups()[group_index] + + return None + + +def popen(command: str, args: str) -> str: + for directory in gvars.PATH: + executable = os.path.join(directory, command) + if ( + os.path.exists(executable) + and os.access(executable, os.F_OK | os.X_OK) + and not os.path.isdir(executable) + ): + break + else: + executable = command + + if settings.DEBUG >= 3: + gvars.log.debug(f"Running: '{executable} {args}'") + + return call_proc(executable, args) + + +def call_proc(executable: str, args: str) -> str: + if consts.WINDOWS: + cmd = executable + " " + args # type: ignore + else: + cmd = [executable, *shlex.split(args)] # type: ignore + + output: Union[str, bytes] = check_output( + cmd, stderr=DEVNULL, env=gvars.ENV # noqa: S603 + ) + + if settings.DEBUG >= 4: + gvars.log.debug(f"Output from '{executable}' command: {output!s}") + + if isinstance(output, bytes): + output = output.decode("utf-8") + + return output + + +def uuid_convert(mac: int) -> str: + return ":".join(("%012X" % mac)[i : i + 2] for i in range(0, 12, 2)) + + +def fetch_ip_using_dns() -> str: + """ + Determines the IP address of the default network interface. + + Sends a UDP packet to Cloudflare's DNS (``1.1.1.1``), which should go through + the default interface. This populates the source address of the socket, + which we then inspect and return. + """ + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: + s.connect(("1.1.1.1", 53)) + return s.getsockname()[0] diff --git a/tests/test_getmac.py b/tests/test_getmac.py index 76e436e..e13e1cb 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -3,11 +3,9 @@ import pytest -from getmac import get_mac_address, getmac +from getmac import get_mac_address, getmac, utils from getmac.variables import settings, consts, gvars -MAC_RE_COLON = r"([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})" - def test_all_methods_defined_are_in_methods_list(): """Test that all methods present in getmac.py are in the METHODS list.""" @@ -30,72 +28,13 @@ def test_method_platform_strings_are_valid(): assert method.platforms <= getmac.Method.VALID_PLATFORM_NAMES -def test_check_path(): - assert getmac.check_path(__file__) - - -def test_clean_mac(): - assert getmac._clean_mac(None) is None - assert getmac._clean_mac("") is None - assert getmac._clean_mac("00:00:00:00:00:00:00:00:00") is None - assert getmac._clean_mac("00:0000:0000") is None - assert getmac._clean_mac("00000000000000000") is None - assert getmac._clean_mac(" 00-50-56-C0-00-01 ") == "00:50:56:c0:00:01" - assert getmac._clean_mac("000000000000") == "00:00:00:00:00:00" - - -def test_search(get_sample): - text = get_sample("ifconfig.out") - regex = r"HWaddr " + MAC_RE_COLON - assert getmac._search(regex, "") is None - assert getmac._search(regex, text, 0) == "74:d4:35:e9:45:71" - - def test_popen(mocker): mocker.patch.object(gvars, "PATH", []) - m = mocker.patch("getmac.getmac._call_proc", return_value="SUCCESS") - assert getmac._popen("TESTCMD", "ARGS") == "SUCCESS" + m = mocker.patch("getmac.utils.call_proc", return_value="SUCCESS") + assert utils.popen("TESTCMD", "ARGS") == "SUCCESS" m.assert_called_once_with("TESTCMD", "ARGS") -def test_call_proc(mocker): - mocker.patch("getmac.getmac.DEVNULL", "DEVNULL") - mocker.patch.object(gvars, "ENV", "ENV") - - mocker.patch.object(consts, "WINDOWS", True) - m = mocker.patch("getmac.getmac.check_output", return_value="WINSUCCESS") - assert getmac._call_proc("CMD", "arg") == "WINSUCCESS" - m.assert_called_once_with("CMD arg", stderr="DEVNULL", env="ENV") - - mocker.patch.object(consts, "WINDOWS", False) - m = mocker.patch("getmac.getmac.check_output", return_value="YAY") - assert getmac._call_proc("CMD", "arg1 arg2") == "YAY" - m.assert_called_once_with(["CMD", "arg1", "arg2"], stderr="DEVNULL", env="ENV") - - -def test_uuid_convert(): - assert getmac._uuid_convert(2482700837424) == "02:42:0C:80:62:30" - assert getmac._uuid_convert(278094213753144) == "FC:EC:DA:D3:29:38" - - -def test_read_file_return(mocker, get_sample): - data = get_sample("ifconfig.out") - mock_open = mocker.mock_open(read_data=data) - mocker.patch("builtins.open", mock_open) - assert getmac._read_file("ifconfig.out") == data - mock_open.assert_called_once_with("ifconfig.out") - - -def test_read_file_not_exist(): - assert getmac._read_file("DOESNOTEXIST") is None - - -def test_fetch_ip_using_dns(mocker): - m = mocker.patch("socket.socket.__enter__") - m.return_value.getsockname.return_value = ("1.2.3.4", 51327) - assert getmac._fetch_ip_using_dns() == "1.2.3.4" - - def test_get_method_by_name(): assert not getmac.get_method_by_name("") assert not getmac.get_method_by_name("invalidmethodname") @@ -175,7 +114,7 @@ def test_initialize_method_cache_platform_override(mocker): mocker.patch("getmac.getmac.FALLBACK_CACHE", {}) mocker.patch.object(consts, "PLATFORM", "windows") mocker.patch.object(settings, "OVERRIDE_PLATFORM", "darwin") - mocker.patch("getmac.getmac.check_command", return_value=True) + mocker.patch("getmac.utils.check_command", return_value=True) assert getmac.initialize_method_cache("iface") assert settings.OVERRIDE_PLATFORM == "darwin" assert consts.PLATFORM == "windows" @@ -189,8 +128,8 @@ def test_initialize_method_cache_no_network_request(mocker): ) mocker.patch("getmac.getmac.FALLBACK_CACHE", {}) mocker.patch.object(consts, "PLATFORM", "linux") - mocker.patch("getmac.getmac.check_command", return_value=True) - mocker.patch("getmac.getmac.check_path", return_value=True) + mocker.patch("getmac.utils.check_command", return_value=True) + mocker.patch("getmac.utils.check_path", return_value=True) assert getmac.initialize_method_cache("ip4", network_request=False) assert consts.PLATFORM == "linux" assert isinstance(getmac.METHOD_CACHE["ip4"], getmac.ArpFile) @@ -209,24 +148,24 @@ def test_get_by_method(mocker, get_sample): # ip4 content = get_sample("windows_10/arp_-a_10.0.0.175.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert getmac.get_by_method("ip4", "10.0.0.175") == "78-28-ca-c4-66-fe" # ip6 content = get_sample("android_9/ip_neighbor.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert ( getmac.get_by_method("ip6", "fe80::8c8f:aaff:fec9:d28b") == "8e:8f:aa:c9:d2:8b" ) # iface content = get_sample("windows_10/wmic_nic.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert getmac.get_by_method("iface", "Ethernet 3") == "00:FF:17:15:F8:C8" # default_iface content = get_sample("openbsd_6/route_nq_show_inet_gateway_priority_1.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert getmac.get_by_method("default_iface") == "em0" @@ -252,7 +191,7 @@ def test_get_by_method_errors(mocker): "default_iface": None, }, ) - mocker.patch("getmac.getmac._read_file", return_value="") + mocker.patch("getmac.utils.read_file", return_value="") mocker.patch.object(settings, "DEBUG", 1) assert getmac.get_by_method("iface", arg="ens33") is None @@ -261,13 +200,13 @@ def test_get_by_method_force_method(mocker): mocker.patch.object(settings, "FORCE_METHOD", "testing123") assert getmac.get_by_method("iface", arg="some_arg") is None - mocker.patch("getmac.getmac._read_file", return_value="00:0c:29:b5:72:37\n") + mocker.patch("getmac.utils.read_file", return_value="00:0c:29:b5:72:37\n") mocker.patch.object(settings, "FORCE_METHOD", "SysIfaceFile") assert getmac.get_by_method("iface", "ens33") == "00:0c:29:b5:72:37\n" def test_get_mac_address_force_method(mocker): - mocker.patch("getmac.getmac._read_file", return_value="00:0c:29:b5:72:37\n") + mocker.patch("getmac.utils.read_file", return_value="00:0c:29:b5:72:37\n") mocker.patch.object(settings, "FORCE_METHOD", "SysIfaceFile") assert getmac.get_mac_address(interface="ens33") == "00:0c:29:b5:72:37" @@ -322,7 +261,7 @@ def test_get_mac_address_default_args_windows_net_request_true(mocker): getmac.get_by_method.assert_called_once_with("iface", "Ethernet") mocker.patch("getmac.getmac.get_by_method", return_value="78:28:ca:c4:66:fe") - mocker.patch("getmac.getmac._fetch_ip_using_dns", return_value="10.0.0.175") + mocker.patch("getmac.utils.fetch_ip_using_dns", return_value="10.0.0.175") assert getmac.get_mac_address(network_request=True) == "78:28:ca:c4:66:fe" getmac.get_by_method.assert_called_once_with("ip4", "10.0.0.175") diff --git a/tests/test_methods.py b/tests/test_methods.py index 73966d2..cf984d4 100644 --- a/tests/test_methods.py +++ b/tests/test_methods.py @@ -6,7 +6,8 @@ import pytest -from getmac import getmac +from getmac import getmac, utils +from getmac.variables import consts # TODO: freebsd11/netstat_-ia.out # TODO: netstat_-ian_aix.out @@ -17,19 +18,19 @@ def test_darwinnetworksetupiface(benchmark, mocker, get_sample): content = get_sample("macos_10.12.6/networksetup_-getmacaddress_en0.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert "08:00:27:2b:c2:ed" == benchmark( getmac.DarwinNetworksetupIface().get, arg="en0" ) - mocker.patch("getmac.getmac._popen", return_value=None) + mocker.patch("getmac.utils.popen", return_value=None) assert not getmac.DarwinNetworksetupIface().get("en0") - mocker.patch("getmac.getmac._popen", return_value="") + mocker.patch("getmac.utils.popen", return_value="") assert not getmac.DarwinNetworksetupIface().get("en0") - mocker.patch("getmac.getmac.check_command", return_value=True) + mocker.patch("getmac.utils.check_command", return_value=True) assert getmac.DarwinNetworksetupIface().test() is True - getmac.check_command.assert_called_once_with("networksetup") + utils.check_command.assert_called_once_with("networksetup") ifconfigether_samples = [ @@ -42,7 +43,7 @@ def test_darwinnetworksetupiface(benchmark, mocker, get_sample): @pytest.mark.parametrize(("mac", "sample_file"), ifconfigether_samples) def test_ifconfigether_darwin(benchmark, mocker, get_sample, mac, sample_file): content = get_sample(sample_file) - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert mac == benchmark(getmac.IfconfigEther().get, arg="en0") if sample_file == "OSX/ifconfig.out": @@ -113,24 +114,24 @@ def test_parse_ifconfig_bad_params(): @pytest.mark.parametrize(("mac", "iface", "sample_file"), ifconfig_samples) def test_ifconfigwithifacearg_samples(mocker, get_sample, mac, iface, sample_file): content = get_sample(sample_file) - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert mac == getmac.IfconfigWithIfaceArg().get(iface) def test_ifconfigwithifacearg_bad_exits(mocker): cpe = CalledProcessError(cmd="ifconfig", returncode=1) - mocker.patch("getmac.getmac._popen", side_effect=cpe) + mocker.patch("getmac.utils.popen", side_effect=cpe) assert getmac.IfconfigWithIfaceArg().get("eth0") is None cpe = CalledProcessError(cmd="ifconfig", returncode=255) - mocker.patch("getmac.getmac._popen", side_effect=cpe) + mocker.patch("getmac.utils.popen", side_effect=cpe) with pytest.raises(CalledProcessError): getmac.IfconfigWithIfaceArg().get("eth0") def test_arping_host_habets(benchmark, mocker, get_sample): content = get_sample("ubuntu_18.04/arping-habets.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) ap = getmac.ArpingHost() ap._is_iputils = False @@ -141,7 +142,7 @@ def test_arping_host_habets(benchmark, mocker, get_sample): def test_arping_host_iputils(benchmark, mocker, get_sample): content = get_sample("ubuntu_18.04/arping-iputils.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) ap = getmac.ArpingHost() ap.get("192.168.16.254") @@ -151,7 +152,7 @@ def test_arping_host_iputils(benchmark, mocker, get_sample): def test_arping_host_busybox(benchmark, mocker, get_sample): content = get_sample("WSL2_kali_2023.1/busybox_arping_-f_-c_1_172-29-16-1.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) ap = getmac.ArpingHost() ap.get("172.29.16.1") @@ -161,19 +162,19 @@ def test_arping_host_busybox(benchmark, mocker, get_sample): def test_windows_10_iface_getmac_exe(benchmark, mocker, get_sample): content = get_sample("windows_10/getmac.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert "74-D4-35-E9-45-71" == benchmark(getmac.GetmacExe().get, arg="Ethernet 2") def test_windows_10_iface_ipconfig(benchmark, mocker, get_sample): content = get_sample("windows_10/ipconfig-all.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert "74-D4-35-E9-45-71" == benchmark(getmac.IpconfigExe().get, arg="Ethernet 3") def test_windows_10_iface_wmic(benchmark, mocker, get_sample): content = get_sample("windows_10/wmic_nic.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert "00:FF:17:15:F8:C8" == benchmark(getmac.WmicExe().get, arg="Ethernet 3") @@ -183,47 +184,47 @@ def test_windows_10_iface_wmic(benchmark, mocker, get_sample): ) def test_arpexe_samples(benchmark, mocker, get_sample, mac, ip, sample_file): content = get_sample(sample_file) - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert mac == benchmark(getmac.ArpExe().get, arg=ip) - mocker.patch("getmac.getmac.check_command", return_value=True) + mocker.patch("getmac.utils.check_command", return_value=True) assert getmac.ArpExe().test() is True - getmac.check_command.assert_called_once_with("arp.exe") + utils.check_command.assert_called_once_with("arp.exe") def test_openbsd_get_default_iface(benchmark, mocker, get_sample): content = get_sample("openbsd_6/route_nq_show_inet_gateway_priority_1.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert "em0" == benchmark(getmac.DefaultIfaceOpenBsd().get) - mocker.patch("getmac.getmac._popen", return_value="") + mocker.patch("getmac.utils.popen", return_value="") assert not getmac.DefaultIfaceOpenBsd().get() - mocker.patch("getmac.getmac.check_command", return_value=True) + mocker.patch("getmac.utils.check_command", return_value=True) assert getmac.DefaultIfaceOpenBsd().test() is True - getmac.check_command.assert_called_once_with("route") + utils.check_command.assert_called_once_with("route") def test_openbsd_remote(benchmark, mocker, get_sample): content = get_sample("openbsd_6/arp_an.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert "52:54:00:12:35:02" == benchmark(getmac.ArpOpenbsd().get, arg="10.0.2.2") assert "52:54:00:12:35:03" == getmac.ArpOpenbsd().get("10.0.2.3") assert "08:00:27:18:64:56" == getmac.ArpOpenbsd().get("10.0.2.15") - mocker.patch("getmac.getmac.check_command", return_value=True) + mocker.patch("getmac.utils.check_command", return_value=True) assert getmac.ArpOpenbsd().test() is True - getmac.check_command.assert_called_once_with("arp") + utils.check_command.assert_called_once_with("arp") def test_freebsd_get_default_iface(benchmark, mocker, get_sample): content = get_sample("freebsd11/netstat_r.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert "em0" == benchmark(getmac.DefaultIfaceFreeBsd().get) - mocker.patch("getmac.getmac.check_command", return_value=True) + mocker.patch("getmac.utils.check_command", return_value=True) assert getmac.DefaultIfaceFreeBsd().test() is True - getmac.check_command.assert_called_once_with("netstat") + utils.check_command.assert_called_once_with("netstat") @pytest.mark.parametrize( @@ -236,7 +237,7 @@ def test_freebsd_get_default_iface(benchmark, mocker, get_sample): ) def test_arpfreebsd_samples(benchmark, mocker, get_sample, mac, ip, sample_file): content = get_sample(sample_file) - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert mac == benchmark(getmac.ArpFreebsd().get, arg=ip) assert not getmac.ArpFreebsd().get("") @@ -245,9 +246,9 @@ def test_arpfreebsd_samples(benchmark, mocker, get_sample, mac, ip, sample_file) assert not getmac.ArpFreebsd().get(mac) assert not getmac.ArpFreebsd().get("em0") - mocker.patch("getmac.getmac.check_command", return_value=True) + mocker.patch("getmac.utils.check_command", return_value=True) assert getmac.ArpFreebsd().test() is True - getmac.check_command.assert_called_once_with("arp") + utils.check_command.assert_called_once_with("arp") @pytest.mark.parametrize( @@ -265,7 +266,7 @@ def test_arpfreebsd_samples(benchmark, mocker, get_sample, mac, ip, sample_file) ) def test_arpfile_samples(benchmark, mocker, get_sample, mac, ip, sample_file): content = get_sample(sample_file) - mocker.patch("getmac.getmac._read_file", return_value=content) + mocker.patch("getmac.utils.read_file", return_value=content) assert mac == benchmark(getmac.ArpFile().get, arg=ip) assert not getmac.ArpFile().get("0.0.0.0") @@ -273,10 +274,10 @@ def test_arpfile_samples(benchmark, mocker, get_sample, mac, ip, sample_file): assert not getmac.ArpFile().get("") assert not getmac.ArpFile().get(mac) - mocker.patch("getmac.getmac._read_file", return_value=None) + mocker.patch("getmac.utils.read_file", return_value=None) assert not getmac.ArpFile().get(ip) - mocker.patch("getmac.getmac._read_file", return_value="") + mocker.patch("getmac.utils.read_file", return_value="") assert not getmac.ArpFile().get(ip) @@ -298,7 +299,7 @@ def test_arpfile_samples(benchmark, mocker, get_sample, mac, ip, sample_file): ) def test_ipneighshow_samples(benchmark, mocker, get_sample, mac, ip, sample_file): content = get_sample(sample_file) - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert mac == benchmark(getmac.IpNeighborShow().get, arg=ip) assert getmac.IpNeighborShow().get("bad") is None @@ -316,7 +317,7 @@ def test_ipneighshow_samples(benchmark, mocker, get_sample, mac, ip, sample_file ) def test_netstatiface_samples(benchmark, mocker, get_sample, mac, iface, sample_file): content = get_sample(sample_file) - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert mac == benchmark(getmac.NetstatIface().get, arg=iface) assert getmac.NetstatIface().get("lo") is None @@ -332,9 +333,9 @@ def test_netstatiface_samples(benchmark, mocker, get_sample, mac, iface, sample_ # assert getmac.NetstatIface().get("Kernel") is None # assert getmac.NetstatIface().get("e") is None - mocker.patch("getmac.getmac._popen", return_value=None) + mocker.patch("getmac.utils.popen", return_value=None) assert getmac.NetstatIface().get("eth0") is None - mocker.patch("getmac.getmac._popen", return_value=" ") + mocker.patch("getmac.utils.popen", return_value=" ") assert getmac.NetstatIface().get("eth0") is None @@ -342,7 +343,7 @@ def test_ip_link_iface_bad_returncode(mocker, get_sample): """Test the exception handling works for old-style ip link.""" content = get_sample("ip_link_list.out") cpe = CalledProcessError(cmd="", returncode=255) - mocker.patch("getmac.getmac._popen", side_effect=[cpe, content]) + mocker.patch("getmac.utils.popen", side_effect=[cpe, content]) except_method = getmac.IpLinkIface() assert "74:d4:35:e9:45:71" == except_method.get("eth0") @@ -363,7 +364,7 @@ def test_ip_link_iface_wsl(benchmark, mocker, get_sample, expected_mac, iface_ar mocker.patch("getmac.getmac.IpLinkIface._tested_arg", True) mocker.patch("getmac.getmac.IpLinkIface._iface_arg", False) content = get_sample("WSL_ubuntu_18.04/ip_link.out") - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert expected_mac == benchmark(getmac.IpLinkIface().get, arg=iface_arg) @@ -380,7 +381,7 @@ def test_ip_link_iface_wsl(benchmark, mocker, get_sample, expected_mac, iface_ar ) def test_iplinkiface_samples(benchmark, mocker, get_sample, mac, iface, sample_file): content = get_sample(sample_file) - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert mac == benchmark(getmac.IpLinkIface().get, arg=iface) # TODO: IpLinkIface regexes need improvements @@ -406,13 +407,13 @@ def test_default_iface_route_command( benchmark, mocker, get_sample, expected_iface, sample_file ): content = get_sample(sample_file) - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert expected_iface == benchmark(getmac.DefaultIfaceRouteCommand().get) - mocker.patch("getmac.getmac._popen", return_value="") + mocker.patch("getmac.utils.popen", return_value="") assert not getmac.DefaultIfaceRouteCommand().get() - mocker.patch("getmac.getmac._popen", return_value="0.0.0.0 ") + mocker.patch("getmac.utils.popen", return_value="0.0.0.0 ") assert not getmac.DefaultIfaceRouteCommand().get() @@ -428,15 +429,15 @@ def test_defaultifacelinuxroutefile_samples( benchmark, mocker, get_sample, iface, sample_file ): content = get_sample(sample_file) - mocker.patch("getmac.getmac._read_file", return_value=content) + mocker.patch("getmac.utils.read_file", return_value=content) assert benchmark(getmac.DefaultIfaceLinuxRouteFile().get) == iface def test_defaultifacelinuxroutefile(mocker): - mocker.patch("getmac.getmac._read_file", return_value=None) + mocker.patch("getmac.utils.read_file", return_value=None) assert getmac.DefaultIfaceLinuxRouteFile().get() is None - mocker.patch("getmac.getmac._read_file", return_value="") + mocker.patch("getmac.utils.read_file", return_value="") assert getmac.DefaultIfaceLinuxRouteFile().get() is None @@ -450,18 +451,18 @@ def test_defaultifacelinuxroutefile(mocker): ) def test_defaultifaceiproute_samples(benchmark, mocker, get_sample, iface, sample_file): content = get_sample(sample_file) - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert iface == benchmark(getmac.DefaultIfaceIpRoute().get) def test_defaultifaceiproute(mocker): - mocker.patch("getmac.getmac._popen", return_value=None) + mocker.patch("getmac.utils.popen", return_value=None) assert getmac.DefaultIfaceIpRoute().get() is None - mocker.patch("getmac.getmac._popen", return_value="") + mocker.patch("getmac.utils.popen", return_value="") assert getmac.DefaultIfaceIpRoute().get() is None - mocker.patch("getmac.getmac._popen", return_value="asdfalksj3") + mocker.patch("getmac.utils.popen", return_value="asdfalksj3") assert not getmac.DefaultIfaceIpRoute().get() @@ -476,22 +477,22 @@ def test_defaultifaceroutegetcommand_samples( benchmark, mocker, get_sample, iface, sample_file ): content = get_sample(sample_file) - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) assert iface == benchmark(getmac.DefaultIfaceRouteGetCommand().get) - mocker.patch("getmac.getmac._popen", return_value=None) + mocker.patch("getmac.utils.popen", return_value=None) assert not getmac.DefaultIfaceRouteGetCommand().get() - mocker.patch("getmac.getmac._popen", return_value="") + mocker.patch("getmac.utils.popen", return_value="") assert not getmac.DefaultIfaceRouteGetCommand().get() # test with bad input (hit the except indexerror case) - mocker.patch("getmac.getmac._popen", return_value="interface:") + mocker.patch("getmac.utils.popen", return_value="interface:") assert not getmac.DefaultIfaceRouteGetCommand().get() - mocker.patch("getmac.getmac.check_command", return_value=True) + mocker.patch("getmac.utils.check_command", return_value=True) assert getmac.DefaultIfaceRouteGetCommand().test() is True - getmac.check_command.assert_called_once_with("route") + utils.check_command.assert_called_once_with("route") @pytest.mark.parametrize( @@ -507,14 +508,15 @@ def test_defaultifaceroutegetcommand_samples( ) def test_arp_various_args(benchmark, mocker, get_sample, mac, ip, sample_file): content = get_sample(sample_file) - mocker.patch("getmac.getmac._popen", return_value=content) + mocker.patch("getmac.utils.popen", return_value=content) if "OSX" in sample_file: - mocker.patch("getmac.getmac.DARWIN", return_value=True) + mocker.patch.object(consts, "DARWIN", True) elif "solaris" in sample_file: - mocker.patch("getmac.getmac.SOLARIS", return_value=True) + mocker.patch.object(consts, "SOLARIS", True) + result = benchmark(getmac.ArpVariousArgs().get, arg=ip) - # NOTE: Darwin and Solaris will return MACs without leading zeroes, + # NOTE: Darwin and Solaris will return MACs without leading zeroes, # e.g. "58:6d:8f:7:c9:94" instead of "58:6d:8f:07:c9:94" # # It makes more sense to me to just handle the weird mac here @@ -525,16 +527,16 @@ def test_arp_various_args(benchmark, mocker, get_sample, mac, ip, sample_file): elif "solaris" in sample_file: assert result == "52:54:0:12:35:2" if "OSX" in sample_file or "solaris" in sample_file: - result = getmac._clean_mac(result) + result = utils.clean_mac(result) assert mac == result def test_sys_iface_file(mocker): - mocker.patch("getmac.getmac._read_file", return_value="00:0c:29:b5:72:37\n") + mocker.patch("getmac.utils.read_file", return_value="00:0c:29:b5:72:37\n") assert getmac.SysIfaceFile().get("ens33") == "00:0c:29:b5:72:37\n" - mocker.patch("getmac.getmac._read_file", return_value=None) + mocker.patch("getmac.utils.read_file", return_value=None) assert getmac.SysIfaceFile().get("ens33") is None @@ -578,6 +580,6 @@ def test_uuid_lanscan(mocker): assert getmac.UuidLanscan().get("10.0.0.1") is None assert getmac.UuidLanscan().get("en0") is None - mocker.patch("getmac.getmac.check_command", return_value=True) + mocker.patch("getmac.utils.check_command", return_value=True) assert getmac.UuidLanscan().test() is True - getmac.check_command.assert_called_once_with("lanscan") + utils.check_command.assert_called_once_with("lanscan") diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..623a912 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,66 @@ +from getmac import utils +from getmac.variables import consts, gvars + +MAC_RE_COLON = r"([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})" + + +def test_check_path(): + assert utils.check_path(__file__) + + +def test_clean_mac(): + assert utils.clean_mac(None) is None + assert utils.clean_mac("") is None + assert utils.clean_mac(b"") is None + assert utils.clean_mac("00:00:00:00:00:00:00:00:00") is None + assert utils.clean_mac("00:0000:0000") is None + assert utils.clean_mac("00000000000000000") is None + assert utils.clean_mac(" 00-50-56-C0-00-01 ") == "00:50:56:c0:00:01" + assert utils.clean_mac("000000000000") == "00:00:00:00:00:00" + assert utils.clean_mac("00:00:00:00:00:00") == "00:00:00:00:00:00" + assert utils.clean_mac(b"00:00:00:00:00:00") == "00:00:00:00:00:00" + + +def test_read_file_return(mocker, get_sample): + data = get_sample("ifconfig.out") + mock_open = mocker.mock_open(read_data=data) + mocker.patch("builtins.open", mock_open) + assert utils.read_file("ifconfig.out") == data + mock_open.assert_called_once_with("ifconfig.out") + + +def test_read_file_not_exist(): + assert utils.read_file("DOESNOTEXIST") is None + + +def test_search(get_sample): + text = get_sample("ifconfig.out") + regex = r"HWaddr " + MAC_RE_COLON + assert utils.search(regex, "") is None + assert utils.search(regex, text, 0) == "74:d4:35:e9:45:71" + + +def test_call_proc(mocker): + mocker.patch("getmac.utils.DEVNULL", "DEVNULL") + mocker.patch.object(gvars, "ENV", "ENV") + + mocker.patch.object(consts, "WINDOWS", True) + m = mocker.patch("getmac.utils.check_output", return_value="WINSUCCESS") + assert utils.call_proc("CMD", "arg") == "WINSUCCESS" + m.assert_called_once_with("CMD arg", stderr="DEVNULL", env="ENV") + + mocker.patch.object(consts, "WINDOWS", False) + m = mocker.patch("getmac.utils.check_output", return_value="YAY") + assert utils.call_proc("CMD", "arg1 arg2") == "YAY" + m.assert_called_once_with(["CMD", "arg1", "arg2"], stderr="DEVNULL", env="ENV") + + +def test_uuid_convert(): + assert utils.uuid_convert(2482700837424) == "02:42:0C:80:62:30" + assert utils.uuid_convert(278094213753144) == "FC:EC:DA:D3:29:38" + + +def test_fetch_ip_using_dns(mocker): + m = mocker.patch("socket.socket.__enter__") + m.return_value.getsockname.return_value = ("1.2.3.4", 51327) + assert utils.fetch_ip_using_dns() == "1.2.3.4" From e994814353d8460107182edfd45a1d47c74fbb14 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 17 Jul 2024 22:44:02 -0600 Subject: [PATCH 046/112] docstrings! --- docs/TODO.md | 10 +++-- getmac/getmac.py | 40 +++++++++++------- getmac/utils.py | 101 +++++++++++++++++++++++++++++++++++++++++++- getmac/variables.py | 19 ++++++++- 4 files changed, 149 insertions(+), 21 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index a65ed83..4bea4ca 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -5,13 +5,13 @@ - [ ] Add guide on using the modules API, e.g. registering a new method in `getmac.getmac.METHODS`, etc. - [ ] Single page on RTD/publish with GitHub actions built with Sphinx and Furo - [ ] Update docs/usage examples for `get_mac_address()` -- [ ] Document possible values for `PLATFORM` variable -- [ ] Document Method (and subclass) attributes (use Sphinx "#:" comments) +- [x] Document possible values for `PLATFORM` variable +- [x] Document Method (and subclass) attributes (use Sphinx "#:" comments) - [ ] Re-add Man pages (and auto-build them in CI and include in releases and the distributions) - [ ] Document `get_by_method()` - [ ] Document `initialize_method_cache()` - [ ] Auto-generated API docs -- [ ] Add docstrings to all util methods +- [x] Add docstrings to all util methods - Furo, sphinx-autodoc-typehints, sphinx-argparse-cli, sphinx-automodapi, sphinx-copybutton, recommonmark ## Tests @@ -43,7 +43,7 @@ ## Breaking changes (or potentially breaking) -- [ ] Split getmac.py into separate files for methods, utils, etc. +- [x] Split getmac.py into separate files for methods, utils, etc. - [ ] Replace the `UuidArpGetNode` method. It calls 3 commands and is quite inefficient, and doesn't exist in Python 3.9+. We should just take the methods and use directly. - [ ] **Consolidate `ip6` argument into `ip` argument.**. Parse based on `::` character vs `.` character if `str` or via `.version == 4`/`.version == 6` for `ipaddress` objects. - Combine `--ip` and `--ip6` CLI arguments into `--ip` output. this would make it *much* easier to test methods. @@ -104,6 +104,8 @@ # Etc +- [ ] Add [isort](https://pycqa.github.io/isort/) (requires python 3.8+) +- [ ] cache the result of executable checks in `getmac.utils.popen()` - [ ] Refactor the default interface code. Combine the functions into one, move the default fallback logic into the function. - TODO: MAC -> IP. "to_find='mac'"? (create GitHub issue?) diff --git a/getmac/getmac.py b/getmac/getmac.py index 5cadfd2..000ad6d 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -11,13 +11,13 @@ .. code-block:: python :caption: Examples - from getmac import get_mac_address - eth_mac = get_mac_address(interface="eth0") - win_mac = get_mac_address(interface="Ethernet 3") - ip_mac = get_mac_address(ip="192.168.0.1") - ip6_mac = get_mac_address(ip6="::1") - host_mac = get_mac_address(hostname="localhost") - updated_mac = get_mac_address(ip="10.0.0.1", network_request=True) + from getmac import get_mac_address + eth_mac = get_mac_address(interface="eth0") + win_mac = get_mac_address(interface="Ethernet 3") + ip_mac = get_mac_address(ip="192.168.0.1") + ip6_mac = get_mac_address(ip6="::1") + host_mac = get_mac_address(hostname="localhost") + updated_mac = get_mac_address(ip="10.0.0.1", network_request=True) """ @@ -83,7 +83,7 @@ def get(self, arg: str) -> Optional[str]: # noqa: ARG002 internal error with the command, or a bug in the code). Args: - arg (str): What the method should get, such as an IP address + arg: What the method should get, such as an IP address or interface name. In the case of default_iface methods, this is not used and defaults to an empty string. @@ -1068,15 +1068,19 @@ def get_method_by_name(method_name: str) -> Optional[Type[Method]]: def get_instance_from_cache(method_type: str, method_name: str) -> Optional[Method]: """ - Get the class for a named Method from the caches. + Get the class for a named :class:`~getmac.getmac.Method` from the caches. - METHOD_CACHE is checked first, and if that fails, - then any entries in FALLBACK_CACHE are checked. - If both fail, None is returned. + :data:`~getmac.getmac.METHOD_CACHE` is checked first, and if that fails, + then any entries in :data:`~getmac.getmac.FALLBACK_CACHE` are checked. + If both fail, :obj:`None` is returned. Args: - method_type: method type to initialize the cache for. + method_type: what cache should be checked. Allowed values are: ``ip4`` | ``ip6`` | ``iface`` | ``default_iface`` + method_name: name of the method to look for + + Returns: + The cached method, or :obj:`None` if the method was not found """ if str(METHOD_CACHE[method_type]) == method_name: @@ -1126,7 +1130,10 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b method_type: method type to initialize the cache for. Allowed values are: ``ip4`` | ``ip6`` | ``iface`` | ``default_iface`` network_request: if methods that make network requests should be included - (those methods that have the attribute ``network_request`` set to ``True``) + (those methods that have the attribute ``network_request`` set to :obj:`True`) + + Returns: + If the cache was initialized successfully """ if METHOD_CACHE.get(method_type): if settings.DEBUG: @@ -1334,7 +1341,10 @@ def get_by_method( Allowed values are: ``ip4``, ``ip6``, ``iface``, ``default_iface`` arg: Argument to pass to the method, e.g. an interface name or IP address network_request: if methods that make network requests should be included - (those methods that have the attribute ``network_request`` set to ``True``) + (those methods that have the attribute ``network_request`` set to :obj:`True`) + + Returns: + The MAC address string, or :obj:`None` if the operation failed """ if not arg and method_type != "default_iface": gvars.log.error(f"Empty arg for method '{method_type}' (raw value: {arg!r})") diff --git a/getmac/utils.py b/getmac/utils.py index ad3db42..cb5f975 100644 --- a/getmac/utils.py +++ b/getmac/utils.py @@ -17,8 +17,14 @@ def check_command(command: str) -> bool: """ - Check if a command exists using `shutil.which()`. The result of the check + Check if a command exists using :func:`shutil.which`. The result of the check is cached in a global dict to speed up subsequent lookups. + + Args: + command: command to check + + Returns: + If the command exists """ if command not in gvars.CHECK_COMMAND_CACHE: gvars.CHECK_COMMAND_CACHE[command] = bool(which(command, path=gvars.PATH_STR)) @@ -28,6 +34,12 @@ def check_command(command: str) -> bool: def check_path(filepath: str) -> bool: """ Check if the file pointed to by `filepath` exists and is readable. + + Args: + filepath: absolute path of file to check + + Returns: + If the filepath exists and is readable """ return os.path.exists(filepath) and os.access(filepath, os.R_OK) @@ -35,6 +47,19 @@ def check_path(filepath: str) -> bool: def clean_mac(mac: Optional[str]) -> Optional[str]: """ Check and format a string result to be lowercase colon-separated MAC. + + It will clean out any garbage and ensure the length and colons are correct, + and replace ``-`` characters with ``:`` characters. + + If string is invalid after as much cleanup as possible, then :obj:`None` + is returned. The specific issue is logged as a warning. + + Args: + mac: MAC address string to clean + + Returns: + Cleaned and formatted MAC address string, or :obj:`None` if + validation failed. """ if mac is None: return None @@ -81,6 +106,16 @@ def clean_mac(mac: Optional[str]) -> Optional[str]: def read_file(filepath: str) -> Optional[str]: + """ + Open and read a file. + + Args: + filepath: Absolute path of the file to read + + Returns: + Text contents of the file, or :obj:`None` if opening + the file failed. + """ try: with open(filepath) as f: return f.read() @@ -92,6 +127,21 @@ def read_file(filepath: str) -> Optional[str]: def search( regex: str, text: str, group_index: int = 0, flags: int = 0 ) -> Optional[str]: + """ + Search for a regular expression in a string, and return the specified group. + This is thin wrapper around :func:`re.search` with some error handling. + + Args: + regex: regular expression + text: data to search + group_index: what index in the ``groupdict`` to return, + if there are more than 1 + flags: :mod:`re` flags + + Returns: + The result, or :obj:`None` if the parsing failed + or nothing was specified to search. + """ if not text: if settings.DEBUG: gvars.log.debug("No text to _search()") @@ -105,8 +155,29 @@ def search( def popen(command: str, args: str) -> str: + """ + Execute a command with arguments and return the stdout (stderr is discarded). + + Wrapper around :func:`~getmac.utils.call_proc`, with checks + to ensure the command exists and is executable and some debug + logging. This should be used instead of + :func:`~getmac.utils.call_proc`. + + Args: + command: command to run, e.g. ``ping`` or ``ping.exe`` + args: arguments to pass to the command, or empty string + if there are no arguments. + + Returns: + stdout from the command (stderr is discarded) + + Raises: + CalledProcessError: the command failed to execute + """ for directory in gvars.PATH: executable = os.path.join(directory, command) + # TODO: cache the result of these checks? these are system calls + # and they can add up. if ( os.path.exists(executable) and os.access(executable, os.F_OK | os.X_OK) @@ -123,6 +194,22 @@ def popen(command: str, args: str) -> str: def call_proc(executable: str, args: str) -> str: + """ + Wrapper around :func:`subprocess.check_output` with some + logging and type conversion. The reason this and + :func:`~getmac.utils.popen` are separate functions is + for testability. + + Args: + executable: command to run + args: arguments to the command + + Returns: + stdout from the command (stderr is discarded) + + Raises: + CalledProcessError: the command failed to execute + """ if consts.WINDOWS: cmd = executable + " " + args # type: ignore else: @@ -142,6 +229,15 @@ def call_proc(executable: str, args: str) -> str: def uuid_convert(mac: int) -> str: + """ + Convert value output from ``uuid`` internal function into a string. + + Args: + mac: integer value returned from a ``uuid`` function + + Returns: + String with colon-separated MAC address + """ return ":".join(("%012X" % mac)[i : i + 2] for i in range(0, 12, 2)) @@ -152,6 +248,9 @@ def fetch_ip_using_dns() -> str: Sends a UDP packet to Cloudflare's DNS (``1.1.1.1``), which should go through the default interface. This populates the source address of the socket, which we then inspect and return. + + Returns: + IP address of this system's default network interface as a string """ with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: s.connect(("1.1.1.1", 53)) diff --git a/getmac/variables.py b/getmac/variables.py index 8c5edd8..0e4afa5 100644 --- a/getmac/variables.py +++ b/getmac/variables.py @@ -69,12 +69,29 @@ class Constants(VarsClass): hasattr(sys, "getandroidapilevel") or "ANDROID_STORAGE" in os.environ ) - #: Generic platform identifier used for filtering methods + #: Generic platform identifier used for filtering methods. + #: + #: Possible values: + #: + #: - wsl + #: - linux + #: - windows + #: - darwin + #: - openbsd + #: - freebsd + #: - netbsd + #: - sunos + #: - any other values that can be returned by :func:`platform.uname`, + #: converted to lowercase. # TODO: change to "wsl1", since WSL2 method should just work like normal linux PLATFORM: str = "wsl" if (LINUX and WSL1) else _SYST.lower() + #: Regular expression pattern for MAC addresses with ':' characters MAC_RE_COLON: str = r"([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})" + + #: Regular expression pattern for MAC addresses with '-' characters MAC_RE_DASH: str = r"([0-9a-fA-F]{2}(?:-[0-9a-fA-F]{2}){5})" + #: On OSX, some MACs in arp output may have a single digit instead of two #: Examples: "18:4f:32:5a:64:5", "14:cc:20:1a:99:0" #: This can also happen on other platforms, like Solaris From f6ac53fd98f7b88523d16c1b8df59609d382a843 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 17 Jul 2024 23:18:53 -0600 Subject: [PATCH 047/112] decode byte-typed args --- getmac/getmac.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/getmac/getmac.py b/getmac/getmac.py index 000ad6d..26425dc 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -1455,6 +1455,16 @@ def get_mac_address( if (hostname and hostname == "localhost") or (ip and ip == "127.0.0.1"): return "00:00:00:00:00:00" + # Convert bytes to str + if interface and isinstance(interface, bytes): + interface = interface.decode("utf-8") + if ip and isinstance(ip, bytes): + ip = ip.decode("utf-8") + if ip6 and isinstance(ip6, bytes): + ip6 = ip6.decode("utf-8") + if hostname and isinstance(hostname, bytes): + hostname = hostname.decode("utf-8") + # Resolve hostname to an IP address if hostname: # Exceptions will be handled silently and returned as a None From c530b250e4f8e8db0fc57f858ada657b355dc92f Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 17 Jul 2024 23:38:38 -0600 Subject: [PATCH 048/112] Support ipaddress objects --- CHANGELOG.md | 19 +++++++---- docs/TODO.md | 8 ++--- getmac/getmac.py | 87 +++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 88 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 946c96c..ffc3040 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,19 +5,26 @@ **Announcement**: Compatibility with Python versions older than 3.7 (2.7, 3.4, 3.5, and 3.6) is deprecated and will be removed in getmac 1.0.0. If you are stuck on an unsupported Python, consider loosely pinning the version of this package in your dependency list, e.g. `getmac<1.0.0` or `getmac~=0.9.0`. -## UNRELEASED +## 1.0.0 (TBD) ### Added -* Added full support for Python 3.10 and 3.11 -* Added tentative support for Python 3.12 +* Full support for Python 3.10 and 3.11 +* Tentative support for Python 3.12 +* Support for [ipaddress](https://docs.python.org/3/library/ipaddress.html) objects from the Python standard library: `IPv4Address`, `IPv4Interface`, `IPv6Address`, `IPv6Interface` + - These can be used with the `ip` and `ip6` arguments to `get_mac_address()` + - The `ip` argument will accept IPv6 objects and process them as if `ip6` was set. + - If passed a string, `ip` will still treat it like a IPv4 address. This behavior may change in a future release, with `ip` allowing either type of address, and `ip6` argument being deprecated. + - `IPv4Network` and `IPv6Network` will result in a `ValueError`. They're networks, not hosts, and passing them makes no sense. +* Handle `bytes` values for `interface`, `ip`, `ip6`, and `hostname` arguments to `get_mac_address()`. This will just call `decode("utf-8")` on the bytes. This is to ease usage with certain libraries and functions that return strings as bytes. + - If you pass it some weird bytes that aren't decodable as utf-8, it's obviously going to break. Don't be dumb :) ### Changed -* Reduce wheel file size (`.whl`) +* Reduce size of wheel file (`.whl`) ### Removed -* Removed support for Python 2.7. It's time. Supporting 2.7 has become an onerous burden on the project, and has prevented fully supporting 3.10 and 3.11 due to having to stay on an ancient version of pytest. I'm done supporting a 13 year old version of a language. +* Removed support for Python 2.7. It's time. Supporting 2.7 has become an onerous burden on the project, and has prevented fully supporting 3.10 and 3.11 due to having to stay on an ancient version of pytest. I'm done supporting a version of the language that hasn't been updated in nearly 15 years, and has been wholly unsupported for over 5 years. * Removed support for Python 3.4 and 3.5. These are quite old and there were a number of essential features added in 3.6+ that I'd like to use (f-strings, type annotations, mature `pathlib`, etc.) -* Removed support for Python 3.6. setuptools added pyproject.toml support in version 61.0.0, however only version 59 is supported for Python 3.6. This means a choice had to be made between moving to pyproject.toml or supporting Python 3.6. Given 3.6 has been EOL for over a year, I've decided to drop support. +* Removed support for Python 3.6. setuptools added `pyproject.toml` support in version 61.0.0, however only version 59 is supported for Python 3.6. This means a choice had to be made between moving to `pyproject.toml` or supporting Python 3.6. Given 3.6 has been EOL for over a year, I've decided to drop support. * Removed support for Jython. As of Jan 23rd, 2023, [Jython](https://github.com/jython/jython) still does not support Python3, only 2.7. If and when it supports Python 3, I'll re-add support for it. * Removed explicit support for IronPython. [IronPython3 exists](https://github.com/IronLanguages/ironpython3), however I don't have a way to test it in CI. If someone knows of a way to test it in GitHub actions, let me know, and I'm happy to re-add explicit support. * Removed RPM packaging. This wasn't being used anywhere, and hasn't been updated since version 0.6.0. Way back when, I dreamt of putting this in OS package repositories. However, I've now learned that's up to the maintainers of those repos (e.g., Ubuntu or Fedora maintainers), not me. diff --git a/docs/TODO.md b/docs/TODO.md index 4bea4ca..216ae81 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -8,8 +8,8 @@ - [x] Document possible values for `PLATFORM` variable - [x] Document Method (and subclass) attributes (use Sphinx "#:" comments) - [ ] Re-add Man pages (and auto-build them in CI and include in releases and the distributions) -- [ ] Document `get_by_method()` -- [ ] Document `initialize_method_cache()` +- [x] Document `get_by_method()` +- [x] Document `initialize_method_cache()` - [ ] Auto-generated API docs - [x] Add docstrings to all util methods - Furo, sphinx-autodoc-typehints, sphinx-argparse-cli, sphinx-automodapi, sphinx-copybutton, recommonmark @@ -23,7 +23,7 @@ - [ ] Improve CLI tests to ensure output is what's expected (e.g. ensure `--override-port` logs a warning and the value actually gets overridden) ## Features -- [ ] Support `ipaddress` objects, `IPv4Address` and `IPv6Address` +- [x] Support `ipaddress` objects, `IPv4Address` and `IPv6Address` - [ ] Add new method: `get_default_interface()`. This leverages the default interface detection methods to expose a helpful public API. - [ ] FreeBSD default interface: `route get default` - [ ] Support NetBSD @@ -45,6 +45,7 @@ ## Breaking changes (or potentially breaking) - [x] Split getmac.py into separate files for methods, utils, etc. - [ ] Replace the `UuidArpGetNode` method. It calls 3 commands and is quite inefficient, and doesn't exist in Python 3.9+. We should just take the methods and use directly. +- [ ] Raise exceptions on critical failures (stuff that were warnings in 0.9.0), all calls to `_warn_critical()`. - [ ] **Consolidate `ip6` argument into `ip` argument.**. Parse based on `::` character vs `.` character if `str` or via `.version == 4`/`.version == 6` for `ipaddress` objects. - Combine `--ip` and `--ip6` CLI arguments into `--ip` output. this would make it *much* easier to test methods. - keep `-4,`, `-6`, and `--ip6` arguments for backwards-compatibility until 1.1.0 @@ -174,7 +175,6 @@ This is going to be a bit more complicated since the highest metric routes are g - [ ] Support IPv6 remote hosts on windows, and IPv4+IPv6 remote hosts on WSL (see "Platform support" section in this document) - [ ] Refactor to build a local state of the interfaces on the system, and use that as fallback for default lookup of interface with no name. Could also include MACs for faster lookup of future interface queries. Similar to how `netifaces` works, with a dict with interface infos. Properly address https://github.com/GhostofGoes/getmac/issues/78 - [ ] New method for "ip addr"? (this would be useful for CentOS and others as a fallback) -- [ ] Raise exceptions on critical failures (stuff that were warnings in 0.9.0), all calls to `_warn_critical()`. - [ ] Method-specific loggers? dynamically set logger name based on subclass name, so we don't have to manually set it in the string - [ ] Add typing stubs to [typeshed](https://github.com/python/typeshed) once getmac 1.0.0 is released ([guide](https://github.com/python/typeshed/blob/master/CONTRIBUTING.md)) - [ ] Use `__import__()` or `importlib`? diff --git a/getmac/getmac.py b/getmac/getmac.py index 26425dc..b083cfd 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -28,6 +28,14 @@ import struct import traceback import warnings +from ipaddress import ( + IPv4Address, + IPv4Interface, + IPv4Network, + IPv6Address, + IPv6Interface, + IPv6Network, +) from subprocess import CalledProcessError from typing import Dict, List, Optional, Set, Tuple, Type, Union @@ -1398,14 +1406,17 @@ def get_by_method( def get_mac_address( - interface: Optional[str] = None, - ip: Optional[str] = None, - ip6: Optional[str] = None, - hostname: Optional[str] = None, + interface: Union[str, bytes, None] = None, + ip: Union[str, bytes, IPv4Address, IPv4Interface, IPv6Address, IPv6Interface, None] = None, + ip6: Union[str, bytes, IPv6Address, IPv6Interface, None] = None, + hostname: Union[str, bytes, None] = None, network_request: bool = True, ) -> Optional[str]: """ - Get an Unicast IEEE 802 MAC-48 address from a local interface or remote host. + Get a MAC from a local interface or remote host. + + If you want to be pedantic, this is (probably) + a unicast IEEE 802 MAC-48 address. Only ONE of the first four arguments may be used (``interface``,``ip``, ``ip6``, or ``hostname``). @@ -1416,9 +1427,6 @@ def get_mac_address( In getmac 1.0.0, exceptions will be raised if the method cache initialization fails (in other words, if there are no valid methods found for the type of MAC requested). - .. warning:: - You MUST provide :class:`str` typed arguments, REGARDLESS of Python version - .. note:: ``"localhost"`` or ``"127.0.0.1"`` will always return ``"00:00:00:00:00:00"`` @@ -1432,11 +1440,17 @@ def get_mac_address( Exceptions raised by methods are handled silently and returned as :obj:`None`. Args: - interface (str): Name of a local network interface (e.g "Ethernet 3", "eth0", "ens32") - ip (str): Canonical dotted decimal IPv4 address of a remote host (e.g ``192.168.0.1``) - ip6 (str): Canonical shortened IPv6 address of a remote host (e.g ``ff02::1:ffe7:7f19``) - hostname (str): DNS hostname of a remote host (e.g "router1.mycorp.com", "localhost") - network_request (bool): If network requests should be made when attempting to find the + interface: Name of a local network interface (e.g "Ethernet 3", "eth0", "ens32") + ip: Canonical dotted decimal IPv4 address of a remote host (e.g ``192.168.0.1``), + or a :mod:`ipaddress` object (:class:`~ipaddress.IPv4Address` or + :class:`~ipaddress.IPv4Interface`). This will also accept + :class:`~ipaddress.IPv6Address` and :class:`~ipaddress.IPv6Interface`, + and treat them as if ``ip6`` argument was set instead. + ip6: Canonical shortened IPv6 address of a remote host (e.g ``ff02::1:ffe7:7f19``), + or a :mod:`ipaddress` object (:class:`~ipaddress.IPv6Address` + and :class:`~ipaddress.IPv6Interface`). + hostname: DNS hostname of a remote host (e.g "router1.mycorp.com", "localhost") + network_request: If network requests should be made when attempting to find the MAC of a remote host. If the ``arping`` command is available, this will be used. If not, a UDP packet will be sent to the remote host to populate the ARP/NDP tables for IPv4/IPv6. The port this packet is sent to can @@ -1447,14 +1461,12 @@ def get_mac_address( found or there was an error. """ + # If debugging, start the timer if settings.DEBUG: import timeit start_time = timeit.default_timer() - if (hostname and hostname == "localhost") or (ip and ip == "127.0.0.1"): - return "00:00:00:00:00:00" - # Convert bytes to str if interface and isinstance(interface, bytes): interface = interface.decode("utf-8") @@ -1465,6 +1477,37 @@ def get_mac_address( if hostname and isinstance(hostname, bytes): hostname = hostname.decode("utf-8") + # Handle ipaddress objects + if ip and not isinstance(ip, str): + if isinstance(ip, IPv4Address): + ip = str(ip) + elif isinstance(ip, IPv4Interface): + ip = str(ip.ip) + elif isinstance(ip, IPv4Network): + raise ValueError( + "IPv4Network objects are not supported. getmac needs a host address, " + "not a network. Try IPv4Address or IPv4Interface instead." + ) + # If IPv6 objects are passed to the ip argument, + # convert them to strings and assign to ip6, and + # unassign ip. + elif isinstance(ip, IPv6Address): + ip6 = str(ip) + ip = None + elif isinstance(ip, IPv6Interface): + ip6 = str(ip.ip) + ip = None + elif isinstance(ip, IPv6Network): + raise ValueError( + "IPv6Network objects are not supported. getmac needs a host address, " + "not a network. Try IPv6Address or IPv6Interface instead." + ) + else: + raise ValueError(f"Unknown type for 'ip' argument: '{ip.__class__.__name__}'") + + if (hostname and hostname == "localhost") or (ip and ip == "127.0.0.1"): + return "00:00:00:00:00:00" + # Resolve hostname to an IP address if hostname: # Exceptions will be handled silently and returned as a None @@ -1479,14 +1522,26 @@ def get_mac_address( if ip6: if not socket.has_ipv6: + # TODO: raise exception instead of returning None? gvars.log.error( "Cannot get the MAC address of a IPv6 host: " "IPv6 is not supported on this system" ) return None + elif isinstance(ip6, IPv6Address): + ip6 = str(ip6) + elif isinstance(ip6, IPv6Interface): + ip6 = str(ip6.ip) + elif isinstance(ip6, IPv6Network): + raise ValueError( + "IPv6Network objects are not supported. getmac needs a host address, " + "not a network. Try IPv6Address or IPv6Interface instead." + ) elif ":" not in ip6: gvars.log.error(f"Invalid IPv6 address (no ':'): {ip6}") return None + elif not isinstance(ip6, str): + raise ValueError(f"Unknown type for 'ip6' argument: '{ip6.__class__.__name__}'") mac = None From d97db6b4b515d086130f13244d61a8fce5fa9922 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 17 Jul 2024 23:49:17 -0600 Subject: [PATCH 049/112] make mypy happy --- getmac/getmac.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/getmac/getmac.py b/getmac/getmac.py index b083cfd..5a8339c 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -1407,7 +1407,9 @@ def get_by_method( def get_mac_address( interface: Union[str, bytes, None] = None, - ip: Union[str, bytes, IPv4Address, IPv4Interface, IPv6Address, IPv6Interface, None] = None, + ip: Union[ + str, bytes, IPv4Address, IPv4Interface, IPv6Address, IPv6Interface, None + ] = None, ip6: Union[str, bytes, IPv6Address, IPv6Interface, None] = None, hostname: Union[str, bytes, None] = None, network_request: bool = True, @@ -1468,17 +1470,18 @@ def get_mac_address( start_time = timeit.default_timer() # Convert bytes to str - if interface and isinstance(interface, bytes): + if isinstance(interface, bytes): interface = interface.decode("utf-8") - if ip and isinstance(ip, bytes): + if isinstance(ip, bytes): ip = ip.decode("utf-8") - if ip6 and isinstance(ip6, bytes): + if isinstance(ip6, bytes): ip6 = ip6.decode("utf-8") - if hostname and isinstance(hostname, bytes): + if isinstance(hostname, bytes): hostname = hostname.decode("utf-8") # Handle ipaddress objects - if ip and not isinstance(ip, str): + # "is not None" check makes mypy happier + if ip is not None and not isinstance(ip, str): if isinstance(ip, IPv4Address): ip = str(ip) elif isinstance(ip, IPv4Interface): @@ -1503,7 +1506,9 @@ def get_mac_address( "not a network. Try IPv6Address or IPv6Interface instead." ) else: - raise ValueError(f"Unknown type for 'ip' argument: '{ip.__class__.__name__}'") + raise ValueError( + f"Unknown type for 'ip' argument: '{ip.__class__.__name__}'" + ) if (hostname and hostname == "localhost") or (ip and ip == "127.0.0.1"): return "00:00:00:00:00:00" @@ -1520,7 +1525,7 @@ def get_mac_address( gvars.log.debug(traceback.format_exc()) return None - if ip6: + if ip6 is not None: # "is not None" check makes mypy happier if not socket.has_ipv6: # TODO: raise exception instead of returning None? gvars.log.error( @@ -1528,7 +1533,8 @@ def get_mac_address( "IPv6 is not supported on this system" ) return None - elif isinstance(ip6, IPv6Address): + + if isinstance(ip6, IPv6Address): ip6 = str(ip6) elif isinstance(ip6, IPv6Interface): ip6 = str(ip6.ip) @@ -1537,11 +1543,14 @@ def get_mac_address( "IPv6Network objects are not supported. getmac needs a host address, " "not a network. Try IPv6Address or IPv6Interface instead." ) - elif ":" not in ip6: + elif not isinstance(ip6, str): + raise ValueError( + f"Unknown type for 'ip6' argument: '{ip6.__class__.__name__}'" + ) + + if ":" not in ip6: gvars.log.error(f"Invalid IPv6 address (no ':'): {ip6}") return None - elif not isinstance(ip6, str): - raise ValueError(f"Unknown type for 'ip6' argument: '{ip6.__class__.__name__}'") mac = None From b13b58d912e7083bba1d5587a1645f8c0614df28 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 18 Jul 2024 00:21:18 -0600 Subject: [PATCH 050/112] fix checks for IPInterface objects --- getmac/getmac.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/getmac/getmac.py b/getmac/getmac.py index 5a8339c..9e63b9b 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -1482,10 +1482,12 @@ def get_mac_address( # Handle ipaddress objects # "is not None" check makes mypy happier if ip is not None and not isinstance(ip, str): - if isinstance(ip, IPv4Address): - ip = str(ip) - elif isinstance(ip, IPv4Interface): + # NOTE: IPv4Interface check must be done first, + # since it's a sub-class of IPv4Address. + if isinstance(ip, IPv4Interface): ip = str(ip.ip) + elif isinstance(ip, IPv4Address): + ip = str(ip) elif isinstance(ip, IPv4Network): raise ValueError( "IPv4Network objects are not supported. getmac needs a host address, " @@ -1494,12 +1496,14 @@ def get_mac_address( # If IPv6 objects are passed to the ip argument, # convert them to strings and assign to ip6, and # unassign ip. - elif isinstance(ip, IPv6Address): - ip6 = str(ip) - ip = None + # NOTE: IPv6Interface check must be done first, + # since it's a sub-class of IPv6Address. elif isinstance(ip, IPv6Interface): ip6 = str(ip.ip) ip = None + elif isinstance(ip, IPv6Address): + ip6 = str(ip) + ip = None elif isinstance(ip, IPv6Network): raise ValueError( "IPv6Network objects are not supported. getmac needs a host address, " @@ -1534,10 +1538,12 @@ def get_mac_address( ) return None - if isinstance(ip6, IPv6Address): - ip6 = str(ip6) - elif isinstance(ip6, IPv6Interface): + # NOTE: IPv6Interface check must be done first, + # since it's a sub-class of IPv6Address. + if isinstance(ip6, IPv6Interface): ip6 = str(ip6.ip) + elif isinstance(ip6, IPv6Address): + ip6 = str(ip6) elif isinstance(ip6, IPv6Network): raise ValueError( "IPv6Network objects are not supported. getmac needs a host address, " From 7431f8a2562cfab1fac4c194f932076fcb0633ef Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 18 Jul 2024 00:21:37 -0600 Subject: [PATCH 051/112] add tests for new functionalities --- docs/TODO.md | 33 +++++++++---------- getmac/variables.py | 2 -- tests/test_getmac.py | 75 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 21 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 216ae81..eab42bf 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -12,7 +12,7 @@ - [x] Document `initialize_method_cache()` - [ ] Auto-generated API docs - [x] Add docstrings to all util methods -- Furo, sphinx-autodoc-typehints, sphinx-argparse-cli, sphinx-automodapi, sphinx-copybutton, recommonmark +- [ ] Furo, sphinx-autodoc-typehints, sphinx-argparse-cli, sphinx-automodapi, sphinx-copybutton, recommonmark ## Tests - [ ] Add test to ensure only the expected files make it into the sdist and wheel, no unexpected files @@ -25,23 +25,9 @@ ## Features - [x] Support `ipaddress` objects, `IPv4Address` and `IPv6Address` - [ ] Add new method: `get_default_interface()`. This leverages the default interface detection methods to expose a helpful public API. -- [ ] FreeBSD default interface: `route get default` -- [ ] Support NetBSD - - platform: `netbsd` - - default interface: `wm0` - - ip: "route -nq show", "netstat -r", arp -a - - default interface via `route get default`? -- [ ] Support Solaris - - platform: `sunos` - - default interface; `e1000g0` (NOTE: likely because this is in Vagrant VM) - - `ifconfig` with no arguments DOES NOT work, need `ifconfig -a` - - `netstat` doesn't work with `-e`, but does work with no arguments, `-a` and `-i`. `-n` prevents hostnames from resolving, which is faster. `-i` gives the shortest output (and is fastest), but doesn't give us a MAC address. Providing the interface as an argument also doesn't work to get a MAC (`netstat -a -I e1000g0`). - - default interface via `route get default`? - - no `ip` command - [ ] [issue 77](https://github.com/GhostofGoes/getmac/issues/77): Feature: get all mac addresses - "As I was thinking of adding support to jaraco.net for supporting macOS devices (IP addresses and mac addresses), I thought getmac might be a helpful solution, but as I delved into it, I could see that getmac only returns a single mac, even though there may be multiple on a host. It would be nice if getmac could abstract some of its behaviors, mainly to allow a user to query for all mac addresses represented by the host." - ## Breaking changes (or potentially breaking) - [x] Split getmac.py into separate files for methods, utils, etc. - [ ] Replace the `UuidArpGetNode` method. It calls 3 commands and is quite inefficient, and doesn't exist in Python 3.9+. We should just take the methods and use directly. @@ -62,10 +48,6 @@ ## Enhancements/fixes/misc. - [ ] Properly support WSL2 -- [ ] move more logic out of `get_mac_address()` into individual methods: - - [ ] interface - - [ ] remote host - - [ ] return data cleanup and validation - [ ] address all TODOs in the code - [ ] implement proper default interface detection on Windows - [ ] [issue #76](https://github.com/GhostofGoes/getmac/issues/76): get_mac_address() is caching an old mac address, no longer present in local ARP @@ -171,6 +153,19 @@ This is going to be a bit more complicated since the highest metric routes are g # Post-1.0.0 +- [ ] FreeBSD default interface: `route get default` +- [ ] Support NetBSD + - platform: `netbsd` + - default interface: `wm0` + - ip: "route -nq show", "netstat -r", arp -a + - default interface via `route get default`? +- [ ] Support Solaris + - platform: `sunos` + - default interface; `e1000g0` (NOTE: likely because this is in Vagrant VM) + - `ifconfig` with no arguments DOES NOT work, need `ifconfig -a` + - `netstat` doesn't work with `-e`, but does work with no arguments, `-a` and `-i`. `-n` prevents hostnames from resolving, which is faster. `-i` gives the shortest output (and is fastest), but doesn't give us a MAC address. Providing the interface as an argument also doesn't work to get a MAC (`netstat -a -I e1000g0`). + - default interface via `route get default`? + - no `ip` command - [ ] Support IPv6 hosts: https://www.practicalcodeuse.com/how-to-arp-a-in-ipv6 - [ ] Support IPv6 remote hosts on windows, and IPv4+IPv6 remote hosts on WSL (see "Platform support" section in this document) - [ ] Refactor to build a local state of the interfaces on the system, and use that as fallback for default lookup of interface with no name. Could also include MACs for faster lookup of future interface queries. Similar to how `netifaces` works, with a dict with interface infos. Properly address https://github.com/GhostofGoes/getmac/issues/78 diff --git a/getmac/variables.py b/getmac/variables.py index 0e4afa5..f12d1c6 100644 --- a/getmac/variables.py +++ b/getmac/variables.py @@ -5,8 +5,6 @@ from typing import Dict, List -# TODO: methods to print, dump, etc variables -# TODO: type validation? class VarsClass: pass diff --git a/tests/test_getmac.py b/tests/test_getmac.py index e13e1cb..e387836 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -1,4 +1,12 @@ import inspect +from ipaddress import ( + IPv4Address, + IPv4Interface, + IPv4Network, + IPv6Address, + IPv6Interface, + IPv6Network, +) from subprocess import CalledProcessError import pytest @@ -213,7 +221,9 @@ def test_get_mac_address_force_method(mocker): def test_get_mac_address_localhost(): assert get_mac_address(hostname="localhost") == "00:00:00:00:00:00" + assert get_mac_address(hostname=b"localhost") == "00:00:00:00:00:00" assert get_mac_address(ip="127.0.0.1") == "00:00:00:00:00:00" + assert get_mac_address(ip=b"127.0.0.1") == "00:00:00:00:00:00" result = get_mac_address(hostname="localhost", network_request=False) assert result == "00:00:00:00:00:00" @@ -224,12 +234,42 @@ def test_get_mac_address_interface(mocker): assert getmac.get_mac_address(interface="ens33") == "00:0c:29:b5:72:37" getmac.get_by_method.assert_called_once_with("iface", "ens33") + # bytes + assert getmac.get_mac_address(interface=b"ens33") == "00:0c:29:b5:72:37" + def test_get_mac_address_ip(mocker): mocker.patch("getmac.getmac.get_by_method", return_value="00:01:02:04:00:12") assert getmac.get_mac_address(ip="192.0.2.2") == "00:01:02:04:00:12" getmac.get_by_method.assert_called_once_with("ip4", "192.0.2.2") + # bytes + assert getmac.get_mac_address(ip=b"192.0.2.2") == "00:01:02:04:00:12" + + # IPv4Address + mocker.patch("getmac.getmac.get_by_method", return_value="00:01:02:04:00:55") + assert getmac.get_mac_address(ip=IPv4Address("192.0.2.55")) == "00:01:02:04:00:55" + getmac.get_by_method.assert_called_once_with("ip4", "192.0.2.55") + + # IPv4Interface + mocker.patch("getmac.getmac.get_by_method", return_value="00:01:02:04:00:66") + assert ( + getmac.get_mac_address(ip=IPv4Interface("192.0.2.66/24")) == "00:01:02:04:00:66" + ) + getmac.get_by_method.assert_called_once_with("ip4", "192.0.2.66") + + # IPv6Address + mocker.patch("getmac.getmac.get_by_method", return_value="00:01:02:04:00:33") + assert getmac.get_mac_address(ip=IPv6Address("fe80::33")) == "00:01:02:04:00:33" + getmac.get_by_method.assert_called_once_with("ip6", "fe80::33") + + # IPv6Interface + mocker.patch("getmac.getmac.get_by_method", return_value="00:01:02:04:00:44") + assert ( + getmac.get_mac_address(ip=IPv6Interface("fe80::44/24")) == "00:01:02:04:00:44" + ) + getmac.get_by_method.assert_called_once_with("ip6", "fe80::44") + def test_get_mac_address_ip6(mocker): mocker.patch("socket.has_ipv6", False) @@ -242,6 +282,21 @@ def test_get_mac_address_ip6(mocker): assert getmac.get_mac_address(ip6="fe80::1") == "00:01:02:04:00:00" getmac.get_by_method.assert_called_once_with("ip6", "fe80::1") + # bytes + assert getmac.get_mac_address(ip6=b"fe80::1") == "00:01:02:04:00:00" + + # IPv6Address + mocker.patch("getmac.getmac.get_by_method", return_value="00:01:02:04:00:11") + assert getmac.get_mac_address(ip6=IPv6Address("fe80::11")) == "00:01:02:04:00:11" + getmac.get_by_method.assert_called_once_with("ip6", "fe80::11") + + # IPv6Interface + mocker.patch("getmac.getmac.get_by_method", return_value="00:01:02:04:00:22") + assert ( + getmac.get_mac_address(ip6=IPv6Interface("fe80::22/24")) == "00:01:02:04:00:22" + ) + getmac.get_by_method.assert_called_once_with("ip6", "fe80::22") + def test_get_mac_address_hostname(mocker): cpe = CalledProcessError(cmd="socket.gaierror", returncode=1) @@ -253,6 +308,9 @@ def test_get_mac_address_hostname(mocker): assert getmac.get_mac_address(hostname="test_hostname") == "00:01:02:04:00:22" getmac.get_by_method.assert_called_once_with("ip4", "192.0.2.22") + # bytes + assert getmac.get_mac_address(hostname=b"test_hostname") == "00:01:02:04:00:22" + def test_get_mac_address_default_args_windows_net_request_true(mocker): mocker.patch.object(consts, "WINDOWS", True) @@ -272,3 +330,20 @@ def test_get_mac_address_default_args_fallback_global(mocker): mocker.patch("getmac.getmac.get_by_method", return_value="08:00:27:e8:81:6f") assert getmac.get_mac_address() == "08:00:27:e8:81:6f" getmac.get_by_method.assert_called_once_with("iface", "eth0") + + +def test_get_mac_address_invalid_types(): + with pytest.raises(ValueError, match="IPv4Network"): + getmac.get_mac_address(ip=IPv4Network("192.0.1.0/24")) + + with pytest.raises(ValueError, match="IPv6Network"): + getmac.get_mac_address(ip=IPv6Network("2001:db00::0/24")) + + with pytest.raises(ValueError, match="IPv6Network"): + getmac.get_mac_address(ip6=IPv6Network("2001:db00::0/24")) + + with pytest.raises(ValueError, match="Unknown type for 'ip' argument"): + getmac.get_mac_address(ip=object()) + + with pytest.raises(ValueError, match="Unknown type for 'ip6' argument"): + getmac.get_mac_address(ip6=object()) From 4cf02face64a03850f736bee4769d9bb65568891 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 11:55:17 -0600 Subject: [PATCH 052/112] Begin conversion to PDM --- CHANGELOG.md | 13 +- docs/TODO.md | 5 +- pdm.lock | 1215 ++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 242 +++++----- 4 files changed, 1330 insertions(+), 145 deletions(-) create mode 100644 pdm.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index ffc3040..07f03ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ **NOTE**: if any changes significantly impact your project or use case, please open an issue on [GitHub](https://github.com/GhostofGoes/getmac/issues) or email me (see git commit author info for address). -**Announcement**: Compatibility with Python versions older than 3.7 (2.7, 3.4, 3.5, and 3.6) is deprecated and will be removed in getmac 1.0.0. If you are stuck on an unsupported Python, consider loosely pinning the version of this package in your dependency list, e.g. `getmac<1.0.0` or `getmac~=0.9.0`. +**Announcement**: Compatibility with Python versions older than 3.8 (2.7, 3.4, 3.5, 3.6, 3.7) is deprecated and will be removed in getmac 1.0.0. If you are stuck on an unsupported Python, consider loosely pinning the version of this package in your dependency list, e.g. `getmac<1.0.0` or `getmac~=0.9.0`. ## 1.0.0 (TBD) @@ -25,17 +25,16 @@ * Removed support for Python 2.7. It's time. Supporting 2.7 has become an onerous burden on the project, and has prevented fully supporting 3.10 and 3.11 due to having to stay on an ancient version of pytest. I'm done supporting a version of the language that hasn't been updated in nearly 15 years, and has been wholly unsupported for over 5 years. * Removed support for Python 3.4 and 3.5. These are quite old and there were a number of essential features added in 3.6+ that I'd like to use (f-strings, type annotations, mature `pathlib`, etc.) * Removed support for Python 3.6. setuptools added `pyproject.toml` support in version 61.0.0, however only version 59 is supported for Python 3.6. This means a choice had to be made between moving to `pyproject.toml` or supporting Python 3.6. Given 3.6 has been EOL for over a year, I've decided to drop support. +* Removed support for Python 3.7, a lot of modern tooling no longer works with it as of late 2024. * Removed support for Jython. As of Jan 23rd, 2023, [Jython](https://github.com/jython/jython) still does not support Python3, only 2.7. If and when it supports Python 3, I'll re-add support for it. * Removed explicit support for IronPython. [IronPython3 exists](https://github.com/IronLanguages/ironpython3), however I don't have a way to test it in CI. If someone knows of a way to test it in GitHub actions, let me know, and I'm happy to re-add explicit support. * Removed RPM packaging. This wasn't being used anywhere, and hasn't been updated since version 0.6.0. Way back when, I dreamt of putting this in OS package repositories. However, I've now learned that's up to the maintainers of those repos (e.g., Ubuntu or Fedora maintainers), not me. ### Dev * Renamed `master` branch to `main` -* Switch to the excellent and elegant [Poetry](https://python-poetry.org/) for project management - * Add `pyproject.toml` - * Move most tool configurations to `pyproject.toml` from `tox.ini` - * Remove all `requirement.txt` files - * Remove `setup.py` and `MANIFEST.in` +* Switch to [PDM](https://pdm-project.org) for project management + * Add `pyproject.toml`, consolidated most tool configurations here + * Remove `setup.py`, `MANIFEST.in`, and `requirement.txt` files ## 0.9.5 (07/15/2024) @@ -279,7 +278,7 @@ e.g. '-dd' for DEBUG level 2. ### Changed * **Significant** performance improvement for remote hosts. Previously, the average for `get_mac_address(ip='10.0.0.100')` was 1.71 seconds. -Now, the average is `12.7 miliseconds`, with the special case of a unpopulated +Now, the average is `12.7 milliseconds`, with the special case of a unpopulated arp table being only slightly higher. This was brought about by changes in how the arp table is populated. The original method was to use the host's `ping` command to send an ICMP packet to the host. This took time, diff --git a/docs/TODO.md b/docs/TODO.md index eab42bf..e9a09dc 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -29,7 +29,6 @@ - "As I was thinking of adding support to jaraco.net for supporting macOS devices (IP addresses and mac addresses), I thought getmac might be a helpful solution, but as I delved into it, I could see that getmac only returns a single mac, even though there may be multiple on a host. It would be nice if getmac could abstract some of its behaviors, mainly to allow a user to query for all mac addresses represented by the host." ## Breaking changes (or potentially breaking) -- [x] Split getmac.py into separate files for methods, utils, etc. - [ ] Replace the `UuidArpGetNode` method. It calls 3 commands and is quite inefficient, and doesn't exist in Python 3.9+. We should just take the methods and use directly. - [ ] Raise exceptions on critical failures (stuff that were warnings in 0.9.0), all calls to `_warn_critical()`. - [ ] **Consolidate `ip6` argument into `ip` argument.**. Parse based on `::` character vs `.` character if `str` or via `.version == 4`/`.version == 6` for `ipaddress` objects. @@ -64,9 +63,13 @@ ## Before releasing - [ ] Add a deprecation warning to `get-mac` package, don't publish it for 1.0.0 - [ ] Update supported versions table in [SECURITY.md](../SECURITY.md) +- [ ] Automate publishing to PyPI in GitHub Actions? +- [ ] Edit README.md to append CHANGELOG before publishing to PyPI ## Done for 1.0.0 +- [x] Move to PDM from Poetry +- [x] Split getmac.py into separate files for methods, utils, etc. - [x] rename "master" branch to "main" - [x] Create 0.9.0 branch from master/main so we can submit patch releases if needed - [x] Drop support for python 2.7, 3.4, and 3.5 diff --git a/pdm.lock b/pdm.lock new file mode 100644 index 0000000..1f15234 --- /dev/null +++ b/pdm.lock @@ -0,0 +1,1215 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default", "ci", "dev", "docs", "lint", "test"] +strategy = ["inherit_metadata"] +lock_version = "4.5.0" +content_hash = "sha256:2f04ca707c3bf80f6527ef85ef1c651e148d63ba379c4704e1e6c5838a598070" + +[[metadata.targets]] +requires_python = "~=3.8" + +[[package]] +name = "alabaster" +version = "0.7.16" +requires_python = ">=3.9" +summary = "A light, configurable Sphinx theme" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +files = [ + {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, + {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, +] + +[[package]] +name = "babel" +version = "2.16.0" +requires_python = ">=3.8" +summary = "Internationalization utilities" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "pytz>=2015.7; python_version < \"3.9\"", +] +files = [ + {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, + {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, +] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +requires_python = ">=3.6.0" +summary = "Screen-scraping library" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "soupsieve>1.2", +] +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[[package]] +name = "black" +version = "23.12.1" +requires_python = ">=3.8" +summary = "The uncompromising code formatter." +groups = ["lint"] +dependencies = [ + "click>=8.0.0", + "mypy-extensions>=0.4.3", + "packaging>=22.0", + "pathspec>=0.9.0", + "platformdirs>=2", + "tomli>=1.1.0; python_version < \"3.11\"", + "typing-extensions>=4.0.1; python_version < \"3.11\"", +] +files = [ + {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, + {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, + {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, + {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, + {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, + {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, + {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, + {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, + {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, + {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, + {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, + {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, + {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, + {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, + {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, + {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, + {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, + {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, + {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, + {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, + {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, + {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, +] + +[[package]] +name = "cachetools" +version = "5.5.0" +requires_python = ">=3.7" +summary = "Extensible memoizing collections and decorators" +groups = ["ci", "dev"] +files = [ + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, +] + +[[package]] +name = "certifi" +version = "2024.7.4" +requires_python = ">=3.6" +summary = "Python package for providing Mozilla's CA Bundle." +groups = ["ci", "docs"] +files = [ + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, +] + +[[package]] +name = "chardet" +version = "5.2.0" +requires_python = ">=3.7" +summary = "Universal encoding detector for Python 3" +groups = ["ci", "dev"] +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +requires_python = ">=3.7.0" +summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +groups = ["ci", "docs"] +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +requires_python = ">=3.7" +summary = "Composable command line interface toolkit" +groups = ["lint"] +dependencies = [ + "colorama; platform_system == \"Windows\"", + "importlib-metadata; python_version < \"3.8\"", +] +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[[package]] +name = "codespell" +version = "2.3.0" +requires_python = ">=3.8" +summary = "Codespell" +groups = ["lint"] +files = [ + {file = "codespell-2.3.0-py3-none-any.whl", hash = "sha256:a9c7cef2501c9cfede2110fd6d4e5e62296920efe9abfb84648df866e47f58d1"}, + {file = "codespell-2.3.0.tar.gz", hash = "sha256:360c7d10f75e65f67bad720af7007e1060a5d395670ec11a7ed1fed9dd17471f"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." +groups = ["ci", "dev", "docs", "lint", "test"] +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "commonmark" +version = "0.9.1" +summary = "Python parser for the CommonMark Markdown spec" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "future>=0.14.0; python_version < \"3\"", +] +files = [ + {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, + {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, +] + +[[package]] +name = "coverage" +version = "6.5.0" +requires_python = ">=3.7" +summary = "Code coverage measurement for Python" +groups = ["ci", "test"] +files = [ + {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, + {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, +] + +[[package]] +name = "coverage" +version = "6.5.0" +extras = ["toml"] +requires_python = ">=3.7" +summary = "Code coverage measurement for Python" +groups = ["test"] +dependencies = [ + "coverage==6.5.0", + "tomli; python_full_version <= \"3.11.0a6\"", +] +files = [ + {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, + {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, + {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, + {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, + {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, + {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, + {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, + {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, + {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, + {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, + {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, + {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, + {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, + {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, + {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, + {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, + {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, + {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, + {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, + {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, + {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, + {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, + {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, +] + +[[package]] +name = "coveralls" +version = "3.3.1" +requires_python = ">= 3.5" +summary = "Show coverage stats online via coveralls.io" +groups = ["ci"] +dependencies = [ + "coverage!=6.0.*,!=6.1,!=6.1.1,<7.0,>=4.1", + "docopt>=0.6.1", + "requests>=1.0.0", +] +files = [ + {file = "coveralls-3.3.1-py2.py3-none-any.whl", hash = "sha256:f42015f31d386b351d4226389b387ae173207058832fbf5c8ec4b40e27b16026"}, + {file = "coveralls-3.3.1.tar.gz", hash = "sha256:b32a8bb5d2df585207c119d6c01567b81fba690c9c10a753bfe27a335bfc43ea"}, +] + +[[package]] +name = "distlib" +version = "0.3.8" +summary = "Distribution utilities" +groups = ["ci", "dev"] +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "docopt" +version = "0.6.2" +summary = "Pythonic argument parser, that will make you smile" +groups = ["ci"] +files = [ + {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, +] + +[[package]] +name = "docutils" +version = "0.19" +requires_python = ">=3.7" +summary = "Docutils -- Python Documentation Utilities" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +files = [ + {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, + {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +requires_python = ">=3.7" +summary = "Backport of PEP 654 (exception groups)" +groups = ["test"] +marker = "python_version < \"3.11\"" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[[package]] +name = "execnet" +version = "2.1.1" +requires_python = ">=3.8" +summary = "execnet: rapid multi-Python deployment" +groups = ["test"] +files = [ + {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, + {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, +] + +[[package]] +name = "filelock" +version = "3.15.4" +requires_python = ">=3.8" +summary = "A platform independent file lock." +groups = ["ci", "dev"] +files = [ + {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, + {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, +] + +[[package]] +name = "furo" +version = "2024.8.6" +requires_python = ">=3.8" +summary = "A clean customisable Sphinx documentation theme." +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "beautifulsoup4", + "pygments>=2.7", + "sphinx-basic-ng>=1.0.0.beta2", + "sphinx<9.0,>=6.0", +] +files = [ + {file = "furo-2024.8.6-py3-none-any.whl", hash = "sha256:6cd97c58b47813d3619e63e9081169880fbe331f0ca883c871ff1f3f11814f5c"}, + {file = "furo-2024.8.6.tar.gz", hash = "sha256:b63e4cee8abfc3136d3bc03a3d45a76a850bada4d6374d24c1716b0e01394a01"}, +] + +[[package]] +name = "idna" +version = "3.8" +requires_python = ">=3.6" +summary = "Internationalized Domain Names in Applications (IDNA)" +groups = ["ci", "docs"] +files = [ + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +summary = "Getting image size from png/jpeg/jpeg2000/gif file" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "importlib-metadata" +version = "8.4.0" +requires_python = ">=3.8" +summary = "Read metadata from Python packages" +groups = ["docs", "test"] +marker = "python_version < \"3.10\"" +dependencies = [ + "typing-extensions>=3.6.4; python_version < \"3.8\"", + "zipp>=0.5", +] +files = [ + {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, + {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +requires_python = ">=3.7" +summary = "brain-dead simple config-ini parsing" +groups = ["test"] +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +requires_python = ">=3.7" +summary = "A very fast and expressive template engine." +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "MarkupSafe>=2.0", +] +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +requires_python = ">=3.7" +summary = "Safely add untrusted strings to HTML/XML markup." +groups = ["docs"] +marker = "python_version >= \"3.9\"" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mypy" +version = "1.11.1" +requires_python = ">=3.8" +summary = "Optional static typing for Python" +groups = ["lint"] +dependencies = [ + "mypy-extensions>=1.0.0", + "tomli>=1.1.0; python_version < \"3.11\"", + "typing-extensions>=4.6.0", +] +files = [ + {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, + {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, + {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, + {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, + {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, + {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, + {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, + {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, + {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, + {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, + {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, + {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, + {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, + {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, + {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, + {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, + {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, + {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, + {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, + {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, + {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, + {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, + {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +requires_python = ">=3.5" +summary = "Type system extensions for programs checked with the mypy type checker." +groups = ["lint"] +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.1" +requires_python = ">=3.8" +summary = "Core utilities for Python packages" +groups = ["ci", "dev", "docs", "lint", "test"] +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +requires_python = ">=3.8" +summary = "Utility library for gitignore style pattern matching of file paths." +groups = ["lint"] +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.2" +requires_python = ">=3.8" +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +groups = ["ci", "dev", "lint"] +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +requires_python = ">=3.8" +summary = "plugin and hook calling mechanisms for python" +groups = ["ci", "dev", "test"] +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[[package]] +name = "psutil" +version = "6.0.0" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +summary = "Cross-platform lib for process and system monitoring in Python." +groups = ["test"] +files = [ + {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, + {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, + {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, + {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, + {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, +] + +[[package]] +name = "py-cpuinfo" +version = "9.0.0" +summary = "Get CPU info with pure Python" +groups = ["test"] +files = [ + {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"}, + {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, +] + +[[package]] +name = "pygments" +version = "2.18.0" +requires_python = ">=3.8" +summary = "Pygments is a syntax highlighting package written in Python." +groups = ["docs", "test"] +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[[package]] +name = "pyproject-api" +version = "1.7.1" +requires_python = ">=3.8" +summary = "API to interact with the python pyproject.toml based projects" +groups = ["ci", "dev"] +dependencies = [ + "packaging>=24.1", + "tomli>=2.0.1; python_version < \"3.11\"", +] +files = [ + {file = "pyproject_api-1.7.1-py3-none-any.whl", hash = "sha256:2dc1654062c2b27733d8fd4cdda672b22fe8741ef1dde8e3a998a9547b071eeb"}, + {file = "pyproject_api-1.7.1.tar.gz", hash = "sha256:7ebc6cd10710f89f4cf2a2731710a98abce37ebff19427116ff2174c9236a827"}, +] + +[[package]] +name = "pytest" +version = "7.4.4" +requires_python = ">=3.7" +summary = "pytest: simple powerful testing with Python" +groups = ["test"] +dependencies = [ + "colorama; sys_platform == \"win32\"", + "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", + "importlib-metadata>=0.12; python_version < \"3.8\"", + "iniconfig", + "packaging", + "pluggy<2.0,>=0.12", + "tomli>=1.0.0; python_version < \"3.11\"", +] +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[[package]] +name = "pytest-benchmark" +version = "4.0.0" +requires_python = ">=3.7" +summary = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." +groups = ["test"] +dependencies = [ + "pathlib2; python_version < \"3.4\"", + "py-cpuinfo", + "pytest>=3.8", + "statistics; python_version < \"3.4\"", +] +files = [ + {file = "pytest-benchmark-4.0.0.tar.gz", hash = "sha256:fb0785b83efe599a6a956361c0691ae1dbb5318018561af10f3e915caa0048d1"}, + {file = "pytest_benchmark-4.0.0-py3-none-any.whl", hash = "sha256:fdb7db64e31c8b277dff9850d2a2556d8b60bcb0ea6524e36e28ffd7c87f71d6"}, +] + +[[package]] +name = "pytest-cov" +version = "5.0.0" +requires_python = ">=3.8" +summary = "Pytest plugin for measuring coverage." +groups = ["test"] +dependencies = [ + "coverage[toml]>=5.2.1", + "pytest>=4.6", +] +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +requires_python = ">=3.8" +summary = "Thin-wrapper around the mock package for easier use with pytest" +groups = ["test"] +dependencies = [ + "pytest>=6.2.5", +] +files = [ + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, +] + +[[package]] +name = "pytest-randomly" +version = "3.15.0" +requires_python = ">=3.8" +summary = "Pytest plugin to randomly order tests and control random.seed." +groups = ["test"] +dependencies = [ + "importlib-metadata>=3.6.0; python_version < \"3.10\"", + "pytest", +] +files = [ + {file = "pytest_randomly-3.15.0-py3-none-any.whl", hash = "sha256:0516f4344b29f4e9cdae8bce31c4aeebf59d0b9ef05927c33354ff3859eeeca6"}, + {file = "pytest_randomly-3.15.0.tar.gz", hash = "sha256:b908529648667ba5e54723088edd6f82252f540cc340d748d1fa985539687047"}, +] + +[[package]] +name = "pytest-xdist" +version = "3.6.1" +requires_python = ">=3.8" +summary = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +groups = ["test"] +dependencies = [ + "execnet>=2.1", + "pytest>=7.0.0", +] +files = [ + {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, + {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, +] + +[[package]] +name = "recommonmark" +version = "0.7.1" +summary = "A docutils-compatibility bridge to CommonMark, enabling you to write CommonMark inside of Docutils & Sphinx projects." +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "commonmark>=0.8.1", + "docutils>=0.11", + "sphinx>=1.3.1", +] +files = [ + {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, + {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +requires_python = ">=3.8" +summary = "Python HTTP for Humans." +groups = ["ci", "docs"] +dependencies = [ + "certifi>=2017.4.17", + "charset-normalizer<4,>=2", + "idna<4,>=2.5", + "urllib3<3,>=1.21.1", +] +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[[package]] +name = "ruff" +version = "0.6.2" +requires_python = ">=3.7" +summary = "An extremely fast Python linter and code formatter, written in Rust." +groups = ["lint"] +files = [ + {file = "ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c"}, + {file = "ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570"}, + {file = "ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1"}, + {file = "ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1"}, + {file = "ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23"}, + {file = "ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a"}, + {file = "ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c"}, + {file = "ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56"}, + {file = "ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da"}, + {file = "ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2"}, + {file = "ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9"}, + {file = "ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +summary = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +groups = ["docs"] +marker = "python_version >= \"3.9\"" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "soupsieve" +version = "2.6" +requires_python = ">=3.8" +summary = "A modern CSS selector implementation for Beautiful Soup." +groups = ["docs"] +marker = "python_version >= \"3.9\"" +files = [ + {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, + {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, +] + +[[package]] +name = "sphinx" +version = "6.2.1" +requires_python = ">=3.8" +summary = "Python documentation generator" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "Jinja2>=3.0", + "Pygments>=2.13", + "alabaster<0.8,>=0.7", + "babel>=2.9", + "colorama>=0.4.5; sys_platform == \"win32\"", + "docutils<0.20,>=0.18.1", + "imagesize>=1.3", + "importlib-metadata>=4.8; python_version < \"3.10\"", + "packaging>=21.0", + "requests>=2.25.0", + "snowballstemmer>=2.0", + "sphinxcontrib-applehelp", + "sphinxcontrib-devhelp", + "sphinxcontrib-htmlhelp>=2.0.0", + "sphinxcontrib-jsmath", + "sphinxcontrib-qthelp", + "sphinxcontrib-serializinghtml>=1.1.5", +] +files = [ + {file = "Sphinx-6.2.1.tar.gz", hash = "sha256:6d56a34697bb749ffa0152feafc4b19836c755d90a7c59b72bc7dfd371b9cc6b"}, + {file = "sphinx-6.2.1-py3-none-any.whl", hash = "sha256:97787ff1fa3256a3eef9eda523a63dbf299f7b47e053cfcf684a1c2a8380c912"}, +] + +[[package]] +name = "sphinx-autodoc-typehints" +version = "1.23.0" +requires_python = ">=3.7" +summary = "Type hints (PEP 484) support for the Sphinx autodoc extension" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "sphinx>=5.3", +] +files = [ + {file = "sphinx_autodoc_typehints-1.23.0-py3-none-any.whl", hash = "sha256:ac099057e66b09e51b698058ba7dd76e57e1fe696cd91b54e121d3dad188f91d"}, + {file = "sphinx_autodoc_typehints-1.23.0.tar.gz", hash = "sha256:5d44e2996633cdada499b6d27a496ddf9dbc95dd1f0f09f7b37940249e61f6e9"}, +] + +[[package]] +name = "sphinx-basic-ng" +version = "1.0.0b2" +requires_python = ">=3.7" +summary = "A modern skeleton for Sphinx themes." +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "sphinx>=4.0", +] +files = [ + {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, + {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, +] + +[[package]] +name = "sphinx-copybutton" +version = "0.5.2" +requires_python = ">=3.7" +summary = "Add a copy button to each of your code cells." +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "sphinx>=1.8", +] +files = [ + {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, + {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, +] + +[[package]] +name = "sphinx-inline-tabs" +version = "2022.1.2b11" +requires_python = ">=3.8" +summary = "Add inline tabbed content to your Sphinx documentation." +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "sphinx>=3", +] +files = [ + {file = "sphinx_inline_tabs-2022.1.2b11-py3-none-any.whl", hash = "sha256:bb4e807769ef52301a186d0678da719120b978a1af4fd62a1e9453684e962dbc"}, + {file = "sphinx_inline_tabs-2022.1.2b11.tar.gz", hash = "sha256:afb9142772ec05ccb07f05d8181b518188fc55631b26ee803c694e812b3fdd73"}, +] + +[[package]] +name = "sphinx-tabs" +version = "3.4.5" +requires_python = "~=3.7" +summary = "Tabbed views for Sphinx" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "docutils", + "pygments", + "sphinx", +] +files = [ + {file = "sphinx-tabs-3.4.5.tar.gz", hash = "sha256:ba9d0c1e3e37aaadd4b5678449eb08176770e0fc227e769b6ce747df3ceea531"}, + {file = "sphinx_tabs-3.4.5-py3-none-any.whl", hash = "sha256:92cc9473e2ecf1828ca3f6617d0efc0aa8acb06b08c56ba29d1413f2f0f6cf09"}, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +requires_python = ">=3.9" +summary = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +files = [ + {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, + {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +requires_python = ">=3.9" +summary = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +files = [ + {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, + {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +requires_python = ">=3.9" +summary = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +files = [ + {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, + {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +requires_python = ">=3.5" +summary = "A sphinx extension which renders display math in HTML via JavaScript" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +requires_python = ">=3.9" +summary = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +files = [ + {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, + {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +requires_python = ">=3.9" +summary = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +files = [ + {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, + {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +requires_python = ">=3.7" +summary = "A lil' TOML parser" +groups = ["ci", "dev", "lint", "test"] +marker = "python_version < \"3.12\"" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tox" +version = "4.18.0" +requires_python = ">=3.8" +summary = "tox is a generic virtualenv management and test command line tool" +groups = ["ci", "dev"] +dependencies = [ + "cachetools>=5.4", + "chardet>=5.2", + "colorama>=0.4.6", + "filelock>=3.15.4", + "packaging>=24.1", + "platformdirs>=4.2.2", + "pluggy>=1.5", + "pyproject-api>=1.7.1", + "tomli>=2.0.1; python_version < \"3.11\"", + "virtualenv>=20.26.3", +] +files = [ + {file = "tox-4.18.0-py3-none-any.whl", hash = "sha256:0a457400cf70615dc0627eb70d293e80cd95d8ce174bb40ac011011f0c03a249"}, + {file = "tox-4.18.0.tar.gz", hash = "sha256:5dfa1cab9f146becd6e351333a82f9e0ade374451630ba65ee54584624c27b58"}, +] + +[[package]] +name = "tox-gh-actions" +version = "3.2.0" +requires_python = ">=3.7" +summary = "Seamless integration of tox into GitHub Actions" +groups = ["ci"] +dependencies = [ + "tox<5,>=4", +] +files = [ + {file = "tox-gh-actions-3.2.0.tar.gz", hash = "sha256:ac6fa3b8da51bc90dd77985fd55f09e746c6558c55910c0a93d643045a2b0ccc"}, + {file = "tox_gh_actions-3.2.0-py2.py3-none-any.whl", hash = "sha256:821b66a4751a788fa3e9617bd796d696507b08c6e1d929ee4faefba06b73b694"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +requires_python = ">=3.8" +summary = "Backported and Experimental Type Hints for Python 3.8+" +groups = ["lint"] +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "urllib3" +version = "2.2.2" +requires_python = ">=3.8" +summary = "HTTP library with thread-safe connection pooling, file post, and more." +groups = ["ci", "docs"] +files = [ + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, +] + +[[package]] +name = "virtualenv" +version = "20.26.3" +requires_python = ">=3.7" +summary = "Virtual Python Environment builder" +groups = ["ci", "dev"] +dependencies = [ + "distlib<1,>=0.3.7", + "filelock<4,>=3.12.2", + "importlib-metadata>=6.6; python_version < \"3.8\"", + "platformdirs<5,>=3.9.1", +] +files = [ + {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, + {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, +] + +[[package]] +name = "vulture" +version = "2.11" +requires_python = ">=3.8" +summary = "Find dead code" +groups = ["lint"] +dependencies = [ + "tomli>=1.1.0; python_version < \"3.11\"", +] +files = [ + {file = "vulture-2.11-py2.py3-none-any.whl", hash = "sha256:12d745f7710ffbf6aeb8279ba9068a24d4e52e8ed333b8b044035c9d6b823aba"}, + {file = "vulture-2.11.tar.gz", hash = "sha256:f0fbb60bce6511aad87ee0736c502456737490a82d919a44e6d92262cb35f1c2"}, +] + +[[package]] +name = "zipp" +version = "3.20.0" +requires_python = ">=3.8" +summary = "Backport of pathlib-compatible object wrapper for zip files" +groups = ["docs", "test"] +marker = "python_version < \"3.10\"" +files = [ + {file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"}, + {file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"}, +] diff --git a/pyproject.toml b/pyproject.toml index d5f704b..27ad1c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,22 @@ [build-system] -requires = ["poetry-core>=1.2.0"] -build-backend = "poetry.core.masonry.api" +requires = ["pdm-backend"] +build-backend = "pdm.backend" [project] name = "getmac" -dynamic = ["version", "readme"] +dynamic = ["version"] # , "readme" +description = "Pure-Python package to get the MAC address of network interfaces and hosts on the local network" +dependencies = [] +readme = "README.md" +# readme = { file = "README.md", content-type = "text/markdown" } authors = [ - { name="Christopher Goes", email="ghostofgoes@gmail.com" } + {name = "Christopher Goes", email = "ghostofgoes@gmail.com"}, +] +maintainers = [ + {name = "Christopher Goes", email = "ghostofgoes@gmail.com"}, ] -description = "Get MAC addresses of remote hosts and local interfaces" -license = { file = "LICENSE" } -requires-python = ">=3.7" +license = { file = "LICENSE"} # text = "MIT" +requires-python = "<4.0,>=3.8" classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", @@ -27,7 +33,6 @@ classifiers = [ "Operating System :: MacOS", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -43,7 +48,7 @@ classifiers = [ "Topic :: System :: Systems Administration", "Topic :: System :: Networking", "Topic :: Utilities", - "Typing :: Typed" + "Typing :: Typed", ] keywords=[ "getmac", @@ -58,7 +63,7 @@ keywords=[ "networking", "layer2", "layer-2", - "802.3" + "802.3", ] [project.urls] @@ -74,169 +79,132 @@ keywords=[ getmac = "getmac.__main__:main" -[tool.poetry] -name = "getmac" -version = "1.0.0a0" -description = "" -license = "MIT" -authors = [ - "Christopher Goes " +[tool.pdm.dev-dependencies] +dev = [ + "tox<5,>=4", ] -maintainers = [ - "Christopher Goes " +ci = [ + "tox<5,>=4", + "tox-gh-actions", + "coveralls", + "tomli; python_version < \"3.12\"", ] -# TODO (before 1.0.0 is released): change to ["README.md", "CHANGELOG.md"] once poetry updates -# https://github.com/python-poetry/poetry/issues/6633 -readme = "README.md" -repository = "https://github.com/GhostofGoes/getmac" -# documentation = "" # TODO -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Environment :: Console", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Operating System :: Microsoft :: Windows", - "Operating System :: POSIX", - "Operating System :: POSIX :: BSD", - "Operating System :: POSIX :: BSD :: FreeBSD", - "Operating System :: POSIX :: BSD :: NetBSD", - "Operating System :: POSIX :: BSD :: OpenBSD", - "Operating System :: POSIX :: Linux", - "Operating System :: POSIX :: Other", - "Operating System :: MacOS", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Intended Audience :: Developers", - "Intended Audience :: System Administrators", - "Intended Audience :: Information Technology", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Software Development :: Libraries", - "Topic :: System :: Systems Administration", - "Topic :: System :: Networking", - "Topic :: Utilities", - "Typing :: Typed" -] -keywords=[ - "getmac", - "get-mac", - "macaddress", - "mac-address", - "mac", - "ethernet", - "mac-48", - "networking", - "network", - "networking", - "layer2", - "layer-2", - "802.3" +test = [ + "pytest<8,>=7", + "pytest-randomly", + "pytest-mock", + "pytest-benchmark", + "pytest-cov", + "pytest-xdist", + "psutil", + "pygments<3.0,>=2.13", ] -packages = [ - { include = "getmac" } +lint = [ + "codespell>=2.2.4", + "tomli; python_version < \"3.12\"", + "ruff", + "vulture", + "black<24.0.0,>=23.3.0", + "mypy<2.0.0,>=1.3.0", ] -include = [ - { path = "tests", format = "sdist" }, - { path = "CHANGELOG.md", format = "sdist" }, - { path = "CONTRIBUTING.md", format = "sdist"}, - { path = "SECURITY.md", format = "sdist" }, - { path = "pyproject.toml", format = "sdist" }, - { path = "tox.ini", format = "sdist" }, - { path = "getmac" }, - { path = "LICENSE" }, - { path = "README.md" }, +docs = [ + "furo; python_version >= \"3.9\"", + "sphinx<7.0.0,>=6.1.3; python_version >= \"3.9\"", + "recommonmark<1.0.0,>=0.7.1; python_version >= \"3.9\"", + "sphinx-autodoc-typehints<2.0.0,>=1.21.8; python_version >= \"3.9\"", + "sphinx-copybutton<1.0.0,>=0.5.1; python_version >= \"3.9\"", + "sphinx-inline-tabs<2023.0.0,>=2022.1.2b11; python_version >= \"3.9\"", + "sphinx-tabs<4.0.0,>=3.4.1; python_version >= \"3.9\"", ] -[tool.poetry.scripts] -getmac = 'getmac.__main__:main' +[tool.pdm.build] +includes = ["getmac/", "LICENSE", "README.md"] +source-includes = [ + "tests/", + "CHANGELOG.md", + "CONTRIBUTING.md", + "SECURITY.md", + "pyproject.toml", + "tox.ini", +] -[tool.poetry.urls] -"Homepage" = "https://github.com/GhostofGoes/getmac" -# TODO: get ReadTheDocs working -# "Documentation" = "https://getmac.readthedocs.io/en/latest/" -"Changelog" = "https://github.com/GhostofGoes/getmac/blob/main/CHANGELOG.md" -"Issue tracker" = "https://github.com/GhostofGoes/getmac/issues" -"Source" = "https://github.com/GhostofGoes/getmac" -"Discord server" = "https://discord.gg/python" +[tool.pdm.version] +source = "file" +path = "getmac/getmac.py" -[tool.poetry.dependencies] -python = "^3.7" -[tool.poetry.group.dev.dependencies] -tox = "^4" +# --- PDM scripts for developers --- +[tool.pdm.scripts.spelling] +help = "Check spelling using Codespell" +cmd = "codespell --toml pyproject.toml" -[tool.poetry.group.ci.dependencies] -tox = "^4" -tox-gh-actions = "*" -coveralls = "*" -tomli = { version = "*", python = "<3.12" } +[tool.pdm.scripts.check_ruff] +help = "Check for linting issues using ruff" +cmd = "ruff check getmac tests" -[tool.poetry.group.test.dependencies] -pytest = "^7" -pytest-randomly = "*" -pytest-mock = "*" -pytest-benchmark = "*" -pytest-cov = "*" -pytest-xdist = "*" -psutil = "*" -pygments = "^2.13" +[tool.pdm.scripts.unused] +help = "Check for unused code using Vulture" +cmd = "vulture --min-confidence 100 getmac tests" -[tool.poetry.group.lint.dependencies] -codespell = ">=2.2.4" -tomli = { version = "*", python = "<3.12" } -ruff = "*" -vulture = "*" -black = "^23.3.0" -mypy = "^1.3.0" +[tool.pdm.scripts.check_format] +help = "Check code formatting is correct" +composite = [ + "black --check --quiet getmac tests", + # TODO: isort +] -[tool.poetry.group.docs.dependencies] -furo = {version = "*", python = ">=3.8,<4.0"} -sphinx = {version = "^6.1.3", python = ">=3.8,<4.0"} -recommonmark = {version = "^0.7.1", python = ">=3.8,<4.0"} -sphinx-autodoc-typehints = {version = "^1.21.8", python = ">=3.8,<4.0"} -sphinx-copybutton = {version = "^0.5.1", python = ">=3.8,<4.0"} -sphinx-inline-tabs = {version = "^2022.1.2b11", python = ">=3.8,<4.0"} -sphinx-tabs = {version = "^3.4.1", python = ">=3.8,<4.0"} +[tool.pdm.scripts.typing] +help = "Check typing using Mypy" +cmd = "mypy getmac tests" +[tool.pdm.scripts.lint] +help = "Run all linting checks (code quality)" +composite = [ + "spelling", + "check_ruff", + "unused", + "check_format", + "typing", +] -[tool.setuptools] -platforms = ["any"] -zip-safe = true -packages = ["getmac"] +[tool.pdm.scripts.format] +help = "Auto-format code to match requirements" +composite = [ + "black --quiet getmac tests", + # TODO: isort +] -[tool.setuptools.dynamic] -version = { attr = "getmac.getmac.__version__" } -# Build the page that will be displayed on PyPI from the README and CHANGELOG -readme = { file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown" } +[tool.pdm.scripts.benchmark] +help = "Run tests with benchmarking enabled" +cmd = "pytest --benchmark-enable -n auto" +# --- Pytest configuration --- [tool.pytest.ini_options] testpaths = "tests" norecursedirs = ".github getmac samples scripts .git .tox .vagrant .idea .vscode dist build *.egg .* .venv" addopts = "--tb=short" + +# --- Coverage configuration --- [tool.coverage.run] relative_files = true # required for coveralls + +# --- Codespell configuration --- [tool.codespell] check-filenames = true skip = '*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,*.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist' ignore-words-list = 'ether,Expresso,expresso,Espresso,espresso' -# https://beta.ruff.rs/docs +# --- Ruff configuration --- +# https://docs.astral.sh/ruff/ [tool.ruff] line-length = 99 # show-source = true -# Rules: https://beta.ruff.rs/docs/rules +# Rules: https://docs.astral.sh/ruff/rules/ # If you violate a rule, lookup the rule on the Rules page in ruff docs. # Many rules have links you can click with a explanation of the rule and how to fix it. # If there isn't a link, go to the project the rule was source from (e.g. flake8-bugbear) From e08edafa1234cd4584cbb7cab012c60dc4eb88cb Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 12:57:43 -0600 Subject: [PATCH 053/112] Update CI for PDM, other misc PDM-related changes --- .github/workflows/ci.yml | 98 +-- CODE_OF_CONDUCT.md | 2 +- docs/TODO.md | 2 + poetry.lock | 1403 -------------------------------------- pyproject.toml | 14 + tox.ini | 3 +- 6 files changed, 54 insertions(+), 1468 deletions(-) delete mode 100644 poetry.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 508aae4..4f64185 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,38 +1,26 @@ name: CI - on: -- release -- push -- pull_request - + - release + - push + - pull_request jobs: lint: name: Run lint checks - runs-on: ubuntu-20.04 - # Workaround to disable pip upgrade warnings until fixed in setup-python - # See: https://github.com/actions/setup-python/issues/532 - env: - PIP_DISABLE_PIP_VERSION_CHECK: 1 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 - - name: Install Python - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Setup PDM + uses: actions/setup-pdm@v4 with: python-version: "3.11" - - name: Install Poetry - # uses: snok/install-poetry@v1 - uses: Gr1N/setup-poetry@v8 - - name: Install dependencies (poetry) - run: poetry install --no-interaction --only=main,ci,lint + cache: true + - name: Install dependencies + run: pdm sync -d -G lint - name: Run lint checks - run: poetry run tox --quiet -e check + run: pdm run lint test: name: Run tests runs-on: ${{ matrix.platform }} - # Workaround to disable pip upgrade warnings until fixed in setup-python - # See: https://github.com/actions/setup-python/issues/532 - env: - PIP_DISABLE_PIP_VERSION_CHECK: 1 strategy: fail-fast: false # Versions available: @@ -42,7 +30,6 @@ jobs: # OS images: https://github.com/actions/runner-images matrix: python-version: - - "3.7" - "3.8" - "3.9" - "3.10" @@ -64,34 +51,26 @@ jobs: architecture: x86 - platform: macos-13 architecture: x86 - # Not sure why, but 3.7 on x86 windows is cursed in CI - # It passes (for now), however it's quite slow, - # and I'm ADHD. - - platform: windows-latest - architecture: x86 - python-version: 3.7 steps: - - uses: actions/checkout@v3 - - name: Install Python - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Setup PDM + uses: actions/setup-pdm@v4 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} - - name: Install Poetry - # uses: snok/install-poetry@v1 - uses: Gr1N/setup-poetry@v8 - name: Display platform and environment information run: | python --version + pdm run python --version python -c "import sys; print('sys.version: %s' % str(sys.version))" python -c "import sys; print('sys.platform: %s' % str(sys.platform))" python -c "import os; print('os.name: %s' % str(os.name))" python -c "import platform; print('platform.uname(): %s' % str(platform.uname()))" - - name: Install dependencies (poetry) - run: poetry install --no-interaction --only=main,ci,test + - name: Install dependencies + run: pdm sync -d -G test,ci # TODO: fix benchmark results (maybe a separate Job?) - name: Run tests - run: poetry run tox --quiet + run: pdm run test - uses: codecov/codecov-action@v3 with: # codecov is annoying. Not sure why I'm still using it, @@ -117,31 +96,26 @@ jobs: # TODO: publish releases using CI # TODO: don't publish pre-releases OR publish them as pre-releases to PyPI -# https://github.com/marketplace/actions/upload-a-build-artifact +# https://docs.pypi.org/trusted-publishers/adding-a-publisher/ +# https://pdm-project.org/en/latest/usage/publish/ # name: "Build and publish a release" # on: # release: # types: [released] # jobs: -# publish: -# name: "📦 Publish Python distributions" -# runs-on: "ubuntu-20.04" -# strategy: -# matrix: -# python-version: -# - "3.8" -# steps: -# - uses: actions/checkout@v3 -# - name: "🐍 Set up Python ${{ matrix.python-version }}" -# uses: actions/setup-python@v4 -# with: -# python-version: "${{ matrix.python-version }}" -# - name: "🐍 Install wheel and build" -# run: "python -m pip install wheel build --user" -# - name: "🐍 Build a binary wheel and a source tarball" -# run: "python -m build" -# - name: "📦 Publish distribution to PyPI" -# uses: "pypa/gh-action-pypi-publish@master" -# if: "startsWith(github.ref, 'refs/tags')" -# with: -# password: "${{ secrets.pypi_password }}" +# pypi-publish: +# name: Publish release to PyPI +# runs-on: "ubuntu-22.04" +# permissions: +# # This permission is needed for private repositories. +# contents: read +# # IMPORTANT: this permission is mandatory for trusted publishing +# id-token: write +# steps: +# - uses: actions/checkout@v4 +# - name: Setup PDM +# uses: actions/setup-pdm@v4 +# with: +# python-version: "3.11" +# - name: Publish package distributions to PyPI +# run: pdm publish diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 0b2d8fe..f581b92 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -5,7 +5,7 @@ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, +identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation. diff --git a/docs/TODO.md b/docs/TODO.md index e9a09dc..d702ca6 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -66,6 +66,8 @@ - [ ] Automate publishing to PyPI in GitHub Actions? - [ ] Edit README.md to append CHANGELOG before publishing to PyPI +## Notes +Removing Tox, maybe. Can do version enumeration in GitHub Actions, for all platforms. No need for me to do locally. ## Done for 1.0.0 - [x] Move to PDM from Poetry diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 68cabad..0000000 --- a/poetry.lock +++ /dev/null @@ -1,1403 +0,0 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. - -[[package]] -name = "alabaster" -version = "0.7.13" -description = "A configurable sidebar-enabled Sphinx theme" -optional = false -python-versions = ">=3.6" -files = [ - {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, - {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, -] - -[[package]] -name = "babel" -version = "2.13.0" -description = "Internationalization utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Babel-2.13.0-py3-none-any.whl", hash = "sha256:fbfcae1575ff78e26c7449136f1abbefc3c13ce542eeb13d43d50d8b047216ec"}, - {file = "Babel-2.13.0.tar.gz", hash = "sha256:04c3e2d28d2b7681644508f836be388ae49e0cfe91465095340395b60d00f210"}, -] - -[package.dependencies] -pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} - -[package.extras] -dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] - -[[package]] -name = "beautifulsoup4" -version = "4.12.2" -description = "Screen-scraping library" -optional = false -python-versions = ">=3.6.0" -files = [ - {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, - {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, -] - -[package.dependencies] -soupsieve = ">1.2" - -[package.extras] -html5lib = ["html5lib"] -lxml = ["lxml"] - -[[package]] -name = "black" -version = "23.3.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.7" -files = [ - {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, - {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, - {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, - {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, - {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, - {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, - {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, - {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, - {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, - {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, - {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, - {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, - {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, - {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "cachetools" -version = "5.3.1" -description = "Extensible memoizing collections and decorators" -optional = false -python-versions = ">=3.7" -files = [ - {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, - {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, -] - -[[package]] -name = "certifi" -version = "2023.7.22" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, -] - -[[package]] -name = "chardet" -version = "5.2.0" -description = "Universal encoding detector for Python 3" -optional = false -python-versions = ">=3.7" -files = [ - {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, - {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.3.0.tar.gz", hash = "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-win32.whl", hash = "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-win32.whl", hash = "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-win32.whl", hash = "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-win32.whl", hash = "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-win32.whl", hash = "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-win32.whl", hash = "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884"}, - {file = "charset_normalizer-3.3.0-py3-none-any.whl", hash = "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2"}, -] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[[package]] -name = "codespell" -version = "2.2.5" -description = "Codespell" -optional = false -python-versions = ">=3.7" -files = [ - {file = "codespell-2.2.5-py3-none-any.whl", hash = "sha256:efa037f54b73c84f7bd14ce8e853d5f822cdd6386ef0ff32e957a3919435b9ec"}, - {file = "codespell-2.2.5.tar.gz", hash = "sha256:6d9faddf6eedb692bf80c9a94ec13ab4f5fb585aabae5f3750727148d7b5be56"}, -] - -[package.extras] -dev = ["Pygments", "build", "chardet", "pytest", "pytest-cov", "pytest-dependency", "ruff", "tomli"] -hard-encoding-detection = ["chardet"] -toml = ["tomli"] -types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "commonmark" -version = "0.9.1" -description = "Python parser for the CommonMark Markdown spec" -optional = false -python-versions = "*" -files = [ - {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, - {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, -] - -[package.extras] -test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] - -[[package]] -name = "coverage" -version = "6.5.0" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, - {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, - {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, - {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, - {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, - {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, - {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, - {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"}, - {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"}, - {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"}, - {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"}, - {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, - {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, - {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, - {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, - {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, - {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, - {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, -] - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "coveralls" -version = "3.3.1" -description = "Show coverage stats online via coveralls.io" -optional = false -python-versions = ">= 3.5" -files = [ - {file = "coveralls-3.3.1-py2.py3-none-any.whl", hash = "sha256:f42015f31d386b351d4226389b387ae173207058832fbf5c8ec4b40e27b16026"}, - {file = "coveralls-3.3.1.tar.gz", hash = "sha256:b32a8bb5d2df585207c119d6c01567b81fba690c9c10a753bfe27a335bfc43ea"}, -] - -[package.dependencies] -coverage = ">=4.1,<6.0.dev0 || >6.1,<6.1.1 || >6.1.1,<7.0" -docopt = ">=0.6.1" -requests = ">=1.0.0" - -[package.extras] -yaml = ["PyYAML (>=3.10)"] - -[[package]] -name = "distlib" -version = "0.3.7" -description = "Distribution utilities" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, - {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, -] - -[[package]] -name = "docopt" -version = "0.6.2" -description = "Pythonic argument parser, that will make you smile" -optional = false -python-versions = "*" -files = [ - {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, -] - -[[package]] -name = "docutils" -version = "0.18.1" -description = "Docutils -- Python Documentation Utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"}, - {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.1.3" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, - {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "execnet" -version = "2.0.2" -description = "execnet: rapid multi-Python deployment" -optional = false -python-versions = ">=3.7" -files = [ - {file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"}, - {file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"}, -] - -[package.extras] -testing = ["hatch", "pre-commit", "pytest", "tox"] - -[[package]] -name = "filelock" -version = "3.12.2" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.7" -files = [ - {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, - {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, -] - -[package.extras] -docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "furo" -version = "2023.9.10" -description = "A clean customisable Sphinx documentation theme." -optional = false -python-versions = ">=3.8" -files = [ - {file = "furo-2023.9.10-py3-none-any.whl", hash = "sha256:513092538537dc5c596691da06e3c370714ec99bc438680edc1debffb73e5bfc"}, - {file = "furo-2023.9.10.tar.gz", hash = "sha256:5707530a476d2a63b8cad83b4f961f3739a69f4b058bcf38a03a39fa537195b2"}, -] - -[package.dependencies] -beautifulsoup4 = "*" -pygments = ">=2.7" -sphinx = ">=6.0,<8.0" -sphinx-basic-ng = "*" - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "imagesize" -version = "1.4.1" -description = "Getting image size from png/jpeg/jpeg2000/gif file" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, - {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, -] - -[[package]] -name = "importlib-metadata" -version = "6.7.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, - {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, -] - -[package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "jinja2" -version = "3.1.2" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "markupsafe" -version = "2.1.3" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.7" -files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, -] - -[[package]] -name = "mypy" -version = "1.4.1" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mypy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8"}, - {file = "mypy-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878"}, - {file = "mypy-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd"}, - {file = "mypy-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc"}, - {file = "mypy-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1"}, - {file = "mypy-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462"}, - {file = "mypy-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258"}, - {file = "mypy-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2"}, - {file = "mypy-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7"}, - {file = "mypy-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01"}, - {file = "mypy-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b"}, - {file = "mypy-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b"}, - {file = "mypy-1.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7"}, - {file = "mypy-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9"}, - {file = "mypy-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042"}, - {file = "mypy-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3"}, - {file = "mypy-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6"}, - {file = "mypy-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f"}, - {file = "mypy-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc"}, - {file = "mypy-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828"}, - {file = "mypy-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3"}, - {file = "mypy-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816"}, - {file = "mypy-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c"}, - {file = "mypy-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f"}, - {file = "mypy-1.4.1-py3-none-any.whl", hash = "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4"}, - {file = "mypy-1.4.1.tar.gz", hash = "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b"}, -] - -[package.dependencies] -mypy-extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} -typing-extensions = ">=4.1.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -install-types = ["pip"] -python2 = ["typed-ast (>=1.4.0,<2)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "packaging" -version = "23.2" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, -] - -[[package]] -name = "pathspec" -version = "0.11.2" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, -] - -[[package]] -name = "platformdirs" -version = "3.11.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = ">=3.7" -files = [ - {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, - {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""} - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] - -[[package]] -name = "pluggy" -version = "1.2.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, - {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "psutil" -version = "5.9.5" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "psutil-5.9.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:be8929ce4313f9f8146caad4272f6abb8bf99fc6cf59344a3167ecd74f4f203f"}, - {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ab8ed1a1d77c95453db1ae00a3f9c50227ebd955437bcf2a574ba8adbf6a74d5"}, - {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4aef137f3345082a3d3232187aeb4ac4ef959ba3d7c10c33dd73763fbc063da4"}, - {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ea8518d152174e1249c4f2a1c89e3e6065941df2fa13a1ab45327716a23c2b48"}, - {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:acf2aef9391710afded549ff602b5887d7a2349831ae4c26be7c807c0a39fac4"}, - {file = "psutil-5.9.5-cp27-none-win32.whl", hash = "sha256:5b9b8cb93f507e8dbaf22af6a2fd0ccbe8244bf30b1baad6b3954e935157ae3f"}, - {file = "psutil-5.9.5-cp27-none-win_amd64.whl", hash = "sha256:8c5f7c5a052d1d567db4ddd231a9d27a74e8e4a9c3f44b1032762bd7b9fdcd42"}, - {file = "psutil-5.9.5-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217"}, - {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da"}, - {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4"}, - {file = "psutil-5.9.5-cp36-abi3-win32.whl", hash = "sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d"}, - {file = "psutil-5.9.5-cp36-abi3-win_amd64.whl", hash = "sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9"}, - {file = "psutil-5.9.5-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30"}, - {file = "psutil-5.9.5.tar.gz", hash = "sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] - -[[package]] -name = "py-cpuinfo" -version = "9.0.0" -description = "Get CPU info with pure Python" -optional = false -python-versions = "*" -files = [ - {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"}, - {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, -] - -[[package]] -name = "pygments" -version = "2.16.1" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, - {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, -] - -[package.extras] -plugins = ["importlib-metadata"] - -[[package]] -name = "pyproject-api" -version = "1.5.3" -description = "API to interact with the python pyproject.toml based projects" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyproject_api-1.5.3-py3-none-any.whl", hash = "sha256:14cf09828670c7b08842249c1f28c8ee6581b872e893f81b62d5465bec41502f"}, - {file = "pyproject_api-1.5.3.tar.gz", hash = "sha256:ffb5b2d7cad43f5b2688ab490de7c4d3f6f15e0b819cb588c4b771567c9729eb"}, -] - -[package.dependencies] -packaging = ">=23.1" -tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "importlib-metadata (>=6.6)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "setuptools (>=67.8)", "wheel (>=0.40)"] - -[[package]] -name = "pytest" -version = "7.4.2" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, - {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-benchmark" -version = "4.0.0" -description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-benchmark-4.0.0.tar.gz", hash = "sha256:fb0785b83efe599a6a956361c0691ae1dbb5318018561af10f3e915caa0048d1"}, - {file = "pytest_benchmark-4.0.0-py3-none-any.whl", hash = "sha256:fdb7db64e31c8b277dff9850d2a2556d8b60bcb0ea6524e36e28ffd7c87f71d6"}, -] - -[package.dependencies] -py-cpuinfo = "*" -pytest = ">=3.8" - -[package.extras] -aspect = ["aspectlib"] -elasticsearch = ["elasticsearch"] -histogram = ["pygal", "pygaljs"] - -[[package]] -name = "pytest-cov" -version = "4.1.0" -description = "Pytest plugin for measuring coverage." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, - {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, -] - -[package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} -pytest = ">=4.6" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] - -[[package]] -name = "pytest-mock" -version = "3.11.1" -description = "Thin-wrapper around the mock package for easier use with pytest" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-mock-3.11.1.tar.gz", hash = "sha256:7f6b125602ac6d743e523ae0bfa71e1a697a2f5534064528c6ff84c2f7c2fc7f"}, - {file = "pytest_mock-3.11.1-py3-none-any.whl", hash = "sha256:21c279fff83d70763b05f8874cc9cfb3fcacd6d354247a976f9529d19f9acf39"}, -] - -[package.dependencies] -pytest = ">=5.0" - -[package.extras] -dev = ["pre-commit", "pytest-asyncio", "tox"] - -[[package]] -name = "pytest-randomly" -version = "3.12.0" -description = "Pytest plugin to randomly order tests and control random.seed." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-randomly-3.12.0.tar.gz", hash = "sha256:d60c2db71ac319aee0fc6c4110a7597d611a8b94a5590918bfa8583f00caccb2"}, - {file = "pytest_randomly-3.12.0-py3-none-any.whl", hash = "sha256:f4f2e803daf5d1ba036cc22bf4fe9dbbf99389ec56b00e5cba732fb5c1d07fdd"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} -pytest = "*" - -[[package]] -name = "pytest-xdist" -version = "3.3.1" -description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-xdist-3.3.1.tar.gz", hash = "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93"}, - {file = "pytest_xdist-3.3.1-py3-none-any.whl", hash = "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2"}, -] - -[package.dependencies] -execnet = ">=1.1" -pytest = ">=6.2.0" - -[package.extras] -psutil = ["psutil (>=3.0)"] -setproctitle = ["setproctitle"] -testing = ["filelock"] - -[[package]] -name = "pytz" -version = "2023.3.post1" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, -] - -[[package]] -name = "recommonmark" -version = "0.7.1" -description = "A docutils-compatibility bridge to CommonMark, enabling you to write CommonMark inside of Docutils & Sphinx projects." -optional = false -python-versions = "*" -files = [ - {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, - {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, -] - -[package.dependencies] -commonmark = ">=0.8.1" -docutils = ">=0.11" -sphinx = ">=1.3.1" - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "ruff" -version = "0.0.292" -description = "An extremely fast Python linter, written in Rust." -optional = false -python-versions = ">=3.7" -files = [ - {file = "ruff-0.0.292-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96"}, - {file = "ruff-0.0.292-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9"}, - {file = "ruff-0.0.292-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a"}, - {file = "ruff-0.0.292-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0"}, - {file = "ruff-0.0.292-py3-none-musllinux_1_2_i686.whl", hash = "sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0"}, - {file = "ruff-0.0.292-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016"}, - {file = "ruff-0.0.292-py3-none-win32.whl", hash = "sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003"}, - {file = "ruff-0.0.292-py3-none-win_amd64.whl", hash = "sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c"}, - {file = "ruff-0.0.292-py3-none-win_arm64.whl", hash = "sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68"}, - {file = "ruff-0.0.292.tar.gz", hash = "sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac"}, -] - -[[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -optional = false -python-versions = "*" -files = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] - -[[package]] -name = "soupsieve" -version = "2.5" -description = "A modern CSS selector implementation for Beautiful Soup." -optional = false -python-versions = ">=3.8" -files = [ - {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, - {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, -] - -[[package]] -name = "sphinx" -version = "6.2.1" -description = "Python documentation generator" -optional = false -python-versions = ">=3.8" -files = [ - {file = "Sphinx-6.2.1.tar.gz", hash = "sha256:6d56a34697bb749ffa0152feafc4b19836c755d90a7c59b72bc7dfd371b9cc6b"}, - {file = "sphinx-6.2.1-py3-none-any.whl", hash = "sha256:97787ff1fa3256a3eef9eda523a63dbf299f7b47e053cfcf684a1c2a8380c912"}, -] - -[package.dependencies] -alabaster = ">=0.7,<0.8" -babel = ">=2.9" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.18.1,<0.20" -imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} -Jinja2 = ">=3.0" -packaging = ">=21.0" -Pygments = ">=2.13" -requests = ">=2.25.0" -snowballstemmer = ">=2.0" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = ">=2.0.0" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = ">=1.1.5" - -[package.extras] -docs = ["sphinxcontrib-websupport"] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] -test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"] - -[[package]] -name = "sphinx-autodoc-typehints" -version = "1.23.0" -description = "Type hints (PEP 484) support for the Sphinx autodoc extension" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sphinx_autodoc_typehints-1.23.0-py3-none-any.whl", hash = "sha256:ac099057e66b09e51b698058ba7dd76e57e1fe696cd91b54e121d3dad188f91d"}, - {file = "sphinx_autodoc_typehints-1.23.0.tar.gz", hash = "sha256:5d44e2996633cdada499b6d27a496ddf9dbc95dd1f0f09f7b37940249e61f6e9"}, -] - -[package.dependencies] -sphinx = ">=5.3" - -[package.extras] -docs = ["furo (>=2022.12.7)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23.4)"] -testing = ["covdefaults (>=2.2.2)", "coverage (>=7.2.2)", "diff-cover (>=7.5)", "nptyping (>=2.5)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "sphobjinv (>=2.3.1)", "typing-extensions (>=4.5)"] -type-comment = ["typed-ast (>=1.5.4)"] - -[[package]] -name = "sphinx-basic-ng" -version = "1.0.0b2" -description = "A modern skeleton for Sphinx themes." -optional = false -python-versions = ">=3.7" -files = [ - {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, - {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, -] - -[package.dependencies] -sphinx = ">=4.0" - -[package.extras] -docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] - -[[package]] -name = "sphinx-copybutton" -version = "0.5.2" -description = "Add a copy button to each of your code cells." -optional = false -python-versions = ">=3.7" -files = [ - {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, - {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, -] - -[package.dependencies] -sphinx = ">=1.8" - -[package.extras] -code-style = ["pre-commit (==2.12.1)"] -rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] - -[[package]] -name = "sphinx-inline-tabs" -version = "2022.1.2b11" -description = "Add inline tabbed content to your Sphinx documentation." -optional = false -python-versions = ">=3.8" -files = [ - {file = "sphinx_inline_tabs-2022.1.2b11-py3-none-any.whl", hash = "sha256:bb4e807769ef52301a186d0678da719120b978a1af4fd62a1e9453684e962dbc"}, - {file = "sphinx_inline_tabs-2022.1.2b11.tar.gz", hash = "sha256:afb9142772ec05ccb07f05d8181b518188fc55631b26ee803c694e812b3fdd73"}, -] - -[package.dependencies] -sphinx = ">=3" - -[package.extras] -doc = ["furo", "myst-parser"] -test = ["pytest", "pytest-cov", "pytest-xdist"] - -[[package]] -name = "sphinx-tabs" -version = "3.4.1" -description = "Tabbed views for Sphinx" -optional = false -python-versions = "~=3.7" -files = [ - {file = "sphinx-tabs-3.4.1.tar.gz", hash = "sha256:d2a09f9e8316e400d57503f6df1c78005fdde220e5af589cc79d493159e1b832"}, - {file = "sphinx_tabs-3.4.1-py3-none-any.whl", hash = "sha256:7cea8942aeccc5d01a995789c01804b787334b55927f29b36ba16ed1e7cb27c6"}, -] - -[package.dependencies] -docutils = ">=0.18.0,<0.19.0" -pygments = "*" -sphinx = "*" - -[package.extras] -code-style = ["pre-commit (==2.13.0)"] -testing = ["bs4", "coverage", "pygments", "pytest (>=7.1,<8)", "pytest-cov", "pytest-regressions", "rinohtype", "sphinx-testing"] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "1.0.4" -description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" -optional = false -python-versions = ">=3.8" -files = [ - {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, - {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "1.0.2" -description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.0.1" -description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, - {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["html5lib", "pytest"] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -description = "A sphinx extension which renders display math in HTML via JavaScript" -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] - -[package.extras] -test = ["flake8", "mypy", "pytest"] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "1.0.3" -description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "1.1.5" -description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, - {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "tox" -version = "4.8.0" -description = "tox is a generic virtualenv management and test command line tool" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tox-4.8.0-py3-none-any.whl", hash = "sha256:4991305a56983d750a0d848a34242be290452aa88d248f1bf976e4036ee8b213"}, - {file = "tox-4.8.0.tar.gz", hash = "sha256:2adacf435b12ccf10b9dfa9975d8ec0afd7cbae44d300463140d2117b968037b"}, -] - -[package.dependencies] -cachetools = ">=5.3.1" -chardet = ">=5.1" -colorama = ">=0.4.6" -filelock = ">=3.12.2" -importlib-metadata = {version = ">=6.7", markers = "python_version < \"3.8\""} -packaging = ">=23.1" -platformdirs = ">=3.9.1" -pluggy = ">=1.2" -pyproject-api = ">=1.5.3" -tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""} -virtualenv = ">=20.24.1" - -[package.extras] -docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.23.3,!=1.23.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.1.1)", "devpi-process (>=0.3.1)", "diff-cover (>=7.7)", "distlib (>=0.3.7)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.17.1)", "psutil (>=5.9.5)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-xdist (>=3.3.1)", "re-assert (>=1.1)", "time-machine (>=2.10)", "wheel (>=0.40)"] - -[[package]] -name = "tox-gh-actions" -version = "3.1.3" -description = "Seamless integration of tox into GitHub Actions" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tox-gh-actions-3.1.3.tar.gz", hash = "sha256:ffd4151fe8b62c6f401a2fc5a01317835d7ab380923f6e0d063c300750308328"}, - {file = "tox_gh_actions-3.1.3-py2.py3-none-any.whl", hash = "sha256:5954766fe2ed0e284f3cdc87535dfdf68d0f803f1011b17ff8cf52ed3156e6c1"}, -] - -[package.dependencies] -tox = ">=4,<5" - -[package.extras] -testing = ["black", "devpi-process", "flake8 (>=6,<7)", "mypy", "pytest (>=7,<8)", "pytest-cov (>=3,<4)", "pytest-mock (>=3,<4)", "pytest-randomly (>=3)"] - -[[package]] -name = "typed-ast" -version = "1.5.5" -description = "a fork of Python 2 and 3 ast modules with type comment support" -optional = false -python-versions = ">=3.6" -files = [ - {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, - {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, - {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, - {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, - {file = "typed_ast-1.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f214394fc1af23ca6d4e9e744804d890045d1643dd7e8229951e0ef39429b5"}, - {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:118c1ce46ce58fda78503eae14b7664163aa735b620b64b5b725453696f2a35c"}, - {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4919b808efa61101456e87f2d4c75b228f4e52618621c77f1ddcaae15904fa"}, - {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fc2b8c4e1bc5cd96c1a823a885e6b158f8451cf6f5530e1829390b4d27d0807f"}, - {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:16f7313e0a08c7de57f2998c85e2a69a642e97cb32f87eb65fbfe88381a5e44d"}, - {file = "typed_ast-1.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2b946ef8c04f77230489f75b4b5a4a6f24c078be4aed241cfabe9cbf4156e7e5"}, - {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, - {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, - {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, - {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, - {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, -] - -[[package]] -name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, -] - -[[package]] -name = "urllib3" -version = "2.0.6" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.7" -files = [ - {file = "urllib3-2.0.6-py3-none-any.whl", hash = "sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2"}, - {file = "urllib3-2.0.6.tar.gz", hash = "sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "virtualenv" -version = "20.24.5" -description = "Virtual Python Environment builder" -optional = false -python-versions = ">=3.7" -files = [ - {file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"}, - {file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -importlib-metadata = {version = ">=6.6", markers = "python_version < \"3.8\""} -platformdirs = ">=3.9.1,<4" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] - -[[package]] -name = "vulture" -version = "2.9.1" -description = "Find dead code" -optional = false -python-versions = ">=3.7" -files = [ - {file = "vulture-2.9.1-py2.py3-none-any.whl", hash = "sha256:a46857014619bd4b785a506b9ddb738cd4621043558309b03a879d18f86e2d72"}, - {file = "vulture-2.9.1.tar.gz", hash = "sha256:b6a2aa632b6fd51488a8eeac650ab4a509bb1a032e81943817a8a2e6a63a30b3"}, -] - -[package.dependencies] -toml = "*" - -[[package]] -name = "zipp" -version = "3.15.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.7" -content-hash = "2d78e0dde61e1a281c3aca20bfe5e26071727ed3abcbb8658c50988c74e39ae9" diff --git a/pyproject.toml b/pyproject.toml index 27ad1c3..f51d782 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -178,6 +178,20 @@ composite = [ help = "Run tests with benchmarking enabled" cmd = "pytest --benchmark-enable -n auto" +# Need more verbosity? Add arguments. +# Example: "pdm run test -vv" (note the "-vv" added) +[tool.pdm.scripts.test] +help = "Run tests (no benchmarking)" +cmd = """ +pytest + --benchmark-disable + --cov=getmac + --cov-report term + --cov-report html + --cov-report xml + -n auto +""" + # --- Pytest configuration --- [tool.pytest.ini_options] diff --git a/tox.ini b/tox.ini index 1d7f5fa..f3d0dc9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,9 @@ [tox] -envlist = py{37,38,39,310,311,312}-{linux,macos,windows} +envlist = py{38,39,310,311,312}-{linux,macos,windows} skip_missing_interpreters = true [gh-actions] python = - 3.7: py37 3.8: py38 3.9: py39 3.10: py310 From 496de624ce3b76787ee233eac2e62a1f41b5c34c Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 12:59:32 -0600 Subject: [PATCH 054/112] lol --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f64185..13e6cf7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Setup PDM - uses: actions/setup-pdm@v4 + uses: pdm-project/setup-pdm@v4 with: python-version: "3.11" cache: true @@ -54,7 +54,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Setup PDM - uses: actions/setup-pdm@v4 + uses: pdm-project/setup-pdm@v4 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} @@ -114,7 +114,7 @@ jobs: # steps: # - uses: actions/checkout@v4 # - name: Setup PDM -# uses: actions/setup-pdm@v4 +# uses: pdm-project/setup-pdm@v4 # with: # python-version: "3.11" # - name: Publish package distributions to PyPI From 7907cb9e980daac14895fcbe65f260431ebbfab7 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 13:08:10 -0600 Subject: [PATCH 055/112] Lint fixes --- getmac/utils.py | 2 +- pyproject.toml | 40 ++++++++++++++++++++++------------------ tests/conftest.py | 2 +- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/getmac/utils.py b/getmac/utils.py index cb5f975..1e36a29 100644 --- a/getmac/utils.py +++ b/getmac/utils.py @@ -216,7 +216,7 @@ def call_proc(executable: str, args: str) -> str: cmd = [executable, *shlex.split(args)] # type: ignore output: Union[str, bytes] = check_output( - cmd, stderr=DEVNULL, env=gvars.ENV # noqa: S603 + cmd, stderr=DEVNULL, env=gvars.ENV ) if settings.DEBUG >= 4: diff --git a/pyproject.toml b/pyproject.toml index f51d782..2b0a436 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -138,10 +138,14 @@ path = "getmac/getmac.py" help = "Check spelling using Codespell" cmd = "codespell --toml pyproject.toml" -[tool.pdm.scripts.check_ruff] +[tool.pdm.scripts.check] help = "Check for linting issues using ruff" cmd = "ruff check getmac tests" +[tool.pdm.scripts.fix] +help = "Check for linting issues and auto-fix" +cmd = "ruff check --fix getmac tests" + [tool.pdm.scripts.unused] help = "Check for unused code using Vulture" cmd = "vulture --min-confidence 100 getmac tests" @@ -161,7 +165,7 @@ cmd = "mypy getmac tests" help = "Run all linting checks (code quality)" composite = [ "spelling", - "check_ruff", + "check", "unused", "check_format", "typing", @@ -216,8 +220,21 @@ ignore-words-list = 'ether,Expresso,expresso,Espresso,espresso' # https://docs.astral.sh/ruff/ [tool.ruff] line-length = 99 -# show-source = true +# Files to exclude from linting +extend-exclude = [ + "*.pyc", + "__pycache__", + ".tox", + ".venv", + ".github", + ".vagrant", + "docs", + "packaging", + "samples", + "scripts", +] +[tool.ruff.lint] # Rules: https://docs.astral.sh/ruff/rules/ # If you violate a rule, lookup the rule on the Rules page in ruff docs. # Many rules have links you can click with a explanation of the rule and how to fix it. @@ -253,20 +270,6 @@ select = [ "RUF", # Ruff-specific rules ] -# Files to exclude from linting -extend-exclude = [ - "*.pyc", - "__pycache__", - ".tox", - ".venv", - ".github", - ".vagrant", - "docs", - "packaging", - "samples", - "scripts", -] - # Linting error codes to ignore ignore = [ "ANN101", # missing-type-self @@ -279,7 +282,8 @@ ignore = [ "PGH003", # Use specific rule codes when ignoring type issues # "PLC1901", # compare-to-empty-string "RUF012", # Mutable class attributes should be annotated with typing.ClassVar + "S603", # subprocess-without-shell-equals-true ] -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "tests/*.py" = ["ANN001", "ANN002", "ANN003", "ANN201", "ANN202", "S101", "S104", "S603"] diff --git a/tests/conftest.py b/tests/conftest.py index 2a8b9de..364da43 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,7 +8,7 @@ settings.DEBUG = 4 -@pytest.fixture() +@pytest.fixture def get_sample(): def _get_sample(sample_path): sdir = path.realpath(path.join(path.dirname(__file__), "samples")) From c5ad762776001e9c6d1e2fade29b10e627d75caf Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 13:20:05 -0600 Subject: [PATCH 056/112] attempt to fix codecov --- .github/workflows/ci.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13e6cf7..321fd8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,12 +71,10 @@ jobs: # TODO: fix benchmark results (maybe a separate Job?) - name: Run tests run: pdm run test - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: - # codecov is annoying. Not sure why I'm still using it, - # may need to look at something that has less heisenbugs. - # https://community.codecov.com/t/ci-failure-due-to-too-many-uploads-to-this-commit/2587/12 fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} - name: Coveralls if: ${{ matrix.platform == 'ubuntu-20.04' && github.event_name != 'pull_request' }} uses: AndreMiras/coveralls-python-action@develop From 762efc3ec062cdc38ac71a0bd4dfbb13094be4b4 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 13:20:22 -0600 Subject: [PATCH 057/112] update readme badges --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5f01168..549e3bd 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ [![Latest version on PyPI](https://badge.fury.io/py/getmac.svg)](https://pypi.org/project/getmac/) [![Coverage Status](https://coveralls.io/repos/github/GhostofGoes/getmac/badge.svg?branch=main)](https://coveralls.io/github/GhostofGoes/getmac?branch=main) +[![codecov](https://codecov.io/github/GhostofGoes/getmac/graph/badge.svg?token=5NxlAE9eGY)](https://codecov.io/github/GhostofGoes/getmac) [![GitHub Actions Pipeline Status](https://github.com/ghostofgoes/getmac/actions/workflows/ci.yml/badge.svg)](https://github.com/GhostofGoes/getmac/actions) [![Weekly PyPI downloads](https://pepy.tech/badge/getmac/week)](https://pepy.tech/project/getmac) [![PyPI downloads](https://pepy.tech/badge/getmac)](https://pepy.tech/project/getmac) -[![PyPI downloadss of the old name](https://pepy.tech/badge/get-mac)](https://pepy.tech/project/get-mac) +[![PyPI downloads of the old name](https://pepy.tech/badge/get-mac)](https://pepy.tech/project/get-mac) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) -[![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/) +[![pdm-managed](https://img.shields.io/endpoint?url=https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2Fpdm-project%2F.github%2Fbadge.json)](https://pdm-project.org) Pure-Python package to get the MAC address of network interfaces and hosts on the local network. From 5d4690c153b8018141bca62f594c04d11586deaa Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 13:40:35 -0600 Subject: [PATCH 058/112] apparently debian python team started packaging it as of getmac 0.9.4 --- README.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 549e3bd..6039989 100644 --- a/README.md +++ b/README.md @@ -29,18 +29,30 @@ If the only system you need to run on is Linux, you can run as root, and C-exten If you want to use `psutil`, `scapy`, or `netifaces`, I have examples of how to do so in a [GitHub Gist](https://gist.github.com/GhostofGoes/0a8e82930e75afcefbd879a825ba4c26). ## Installation -Stable release from PyPI + +### Stable release from PyPI ```bash pip install getmac ``` -Latest development version +### Latest development version ```bash pip install https://github.com/ghostofgoes/getmac/archive/main.tar.gz ``` +### Debian-based distributions +This may have it available, depending on your distro (see packaging status below): + +```bash +sudo apt install getmac +``` + +### OS Packaging Status + +[![Packaging status](https://repology.org/badge/vertical-allrepos/getmac.svg)](https://repology.org/project/getmac/versions) + ## Python examples ```python from getmac import get_mac_address @@ -113,7 +125,7 @@ getmac --ip 192.168.0.1 --override-platform windows getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost ``` -## `get_mac_address()` +## API: `get_mac_address()` - `interface`: Name of a network interface on the system - `ip`: IPv4 address of a remote host - `ip6`: IPv6 address of a remote host @@ -198,6 +210,7 @@ If you are running a old Python (2.6/3.3 and older) or interpreter, then you can - Python 3.4: `getmac<1.0.0` - Python 3.5: `getmac<1.0.0` - Python 3.6: `getmac<1.0.0` +- Python 3.7: `getmac<1.0.0` NOTE: these versions do not have many of the performance improvements, platform support, and bug fixes that came with later releases. They generally work, just not as well. However, if you're using such an old Python, you probably don't care about all that :) @@ -236,7 +249,7 @@ Before submitting a PR, please make sure you've completed the [pull request chec The [Python Discord server](https://discord.gg/python) is a good place to ask questions or discuss the project (Handle: @KnownError#0001). -### Contributors +## Contributors - Christopher Goes (@ghostofgoes) - Author and maintainer - Calvin Tran (@cyberhobbes) - Windows interface detection improvements - Daniel Flanagan (@FlantasticDan) - Code cleanup From 4dd90ca2255e34d0eaea4e46e9a89f9ee107a512 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 13:51:33 -0600 Subject: [PATCH 059/112] add trackgit --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6039989..ecb8c3d 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@ [![PyPI downloads of the old name](https://pepy.tech/badge/get-mac)](https://pepy.tech/project/get-mac) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) [![pdm-managed](https://img.shields.io/endpoint?url=https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2Fpdm-project%2F.github%2Fbadge.json)](https://pdm-project.org) + +trackgit-views + Pure-Python package to get the MAC address of network interfaces and hosts on the local network. From 12523e43d9d4f1a1c0e038c73ef8fa5c3dd200df Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 13:54:20 -0600 Subject: [PATCH 060/112] fix lint failing silently --- getmac/utils.py | 4 +--- pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/getmac/utils.py b/getmac/utils.py index 1e36a29..b08a2c1 100644 --- a/getmac/utils.py +++ b/getmac/utils.py @@ -215,9 +215,7 @@ def call_proc(executable: str, args: str) -> str: else: cmd = [executable, *shlex.split(args)] # type: ignore - output: Union[str, bytes] = check_output( - cmd, stderr=DEVNULL, env=gvars.ENV - ) + output: Union[str, bytes] = check_output(cmd, stderr=DEVNULL, env=gvars.ENV) if settings.DEBUG >= 4: gvars.log.debug(f"Output from '{executable}' command: {output!s}") diff --git a/pyproject.toml b/pyproject.toml index 2b0a436..562ca99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -153,7 +153,7 @@ cmd = "vulture --min-confidence 100 getmac tests" [tool.pdm.scripts.check_format] help = "Check code formatting is correct" composite = [ - "black --check --quiet getmac tests", + "black --check getmac tests", # TODO: isort ] @@ -174,7 +174,7 @@ composite = [ [tool.pdm.scripts.format] help = "Auto-format code to match requirements" composite = [ - "black --quiet getmac tests", + "black getmac tests", # TODO: isort ] From 070493d8371a8d13ae3e537d207aa8296b63b246 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 13:56:48 -0600 Subject: [PATCH 061/112] attempt to fix lint test --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 321fd8f..203e55c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,8 @@ jobs: python-version: "3.11" cache: true - name: Install dependencies - run: pdm sync -d -G lint + # NOTE: test is needed here to evaluate imports for pytest + run: pdm sync -d -G lint,test - name: Run lint checks run: pdm run lint test: From 2462060d5b7d5cefdce776cfe321aada4dd55a65 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 14:05:02 -0600 Subject: [PATCH 062/112] Remove Coveralls (RIP) --- .coveragerc | 5 ----- .github/workflows/ci.yml | 15 -------------- CHANGELOG.md | 1 + README.md | 1 - pdm.lock | 44 +++++++++++----------------------------- pyproject.toml | 6 ------ 6 files changed, 13 insertions(+), 59 deletions(-) delete mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 18390c7..0000000 --- a/.coveragerc +++ /dev/null @@ -1,5 +0,0 @@ -# TODO: this works around a issue in coveralls-python-action -# Once the fix is merged (it's been 2 years, I don't have high hopes), then this file can be removed -# https://github.com/AndreMiras/coveralls-python-action/pull/16 -[run] -relative_files = True \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 203e55c..4ec85b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,21 +76,6 @@ jobs: with: fail_ci_if_error: false token: ${{ secrets.CODECOV_TOKEN }} - - name: Coveralls - if: ${{ matrix.platform == 'ubuntu-20.04' && github.event_name != 'pull_request' }} - uses: AndreMiras/coveralls-python-action@develop - with: - parallel: true - flag-name: ${{ matrix.python-version }}-${{ matrix.platform }} - coveralls_finish: - needs: test - runs-on: ubuntu-20.04 - steps: - - name: Coveralls Finished - if: ${{ github.event_name != 'pull_request' }} - uses: AndreMiras/coveralls-python-action@develop - with: - parallel-finished: true # TODO: publish releases using CI diff --git a/CHANGELOG.md b/CHANGELOG.md index 07f03ea..438786c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ * Switch to [PDM](https://pdm-project.org) for project management * Add `pyproject.toml`, consolidated most tool configurations here * Remove `setup.py`, `MANIFEST.in`, and `requirement.txt` files +* Switch to CodeCov from Coveralls ## 0.9.5 (07/15/2024) diff --git a/README.md b/README.md index ecb8c3d..7ec7327 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ [![Latest version on PyPI](https://badge.fury.io/py/getmac.svg)](https://pypi.org/project/getmac/) -[![Coverage Status](https://coveralls.io/repos/github/GhostofGoes/getmac/badge.svg?branch=main)](https://coveralls.io/github/GhostofGoes/getmac?branch=main) [![codecov](https://codecov.io/github/GhostofGoes/getmac/graph/badge.svg?token=5NxlAE9eGY)](https://codecov.io/github/GhostofGoes/getmac) [![GitHub Actions Pipeline Status](https://github.com/ghostofgoes/getmac/actions/workflows/ci.yml/badge.svg)](https://github.com/GhostofGoes/getmac/actions) [![Weekly PyPI downloads](https://pepy.tech/badge/getmac/week)](https://pepy.tech/project/getmac) diff --git a/pdm.lock b/pdm.lock index 1f15234..c0c7a47 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "ci", "dev", "docs", "lint", "test"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:2f04ca707c3bf80f6527ef85ef1c651e148d63ba379c4704e1e6c5838a598070" +content_hash = "sha256:530dcdba010d4d7600ca88a7ba75d8d971fa187fd7f26ce6ecd7f1b5f0827b20" [[metadata.targets]] requires_python = "~=3.8" @@ -108,7 +108,8 @@ name = "certifi" version = "2024.7.4" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." -groups = ["ci", "docs"] +groups = ["docs"] +marker = "python_version >= \"3.9\"" files = [ {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, @@ -130,7 +131,8 @@ name = "charset-normalizer" version = "3.3.2" requires_python = ">=3.7.0" summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -groups = ["ci", "docs"] +groups = ["docs"] +marker = "python_version >= \"3.9\"" files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, @@ -267,7 +269,7 @@ name = "coverage" version = "6.5.0" requires_python = ">=3.7" summary = "Code coverage measurement for Python" -groups = ["ci", "test"] +groups = ["test"] files = [ {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, @@ -367,22 +369,6 @@ files = [ {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, ] -[[package]] -name = "coveralls" -version = "3.3.1" -requires_python = ">= 3.5" -summary = "Show coverage stats online via coveralls.io" -groups = ["ci"] -dependencies = [ - "coverage!=6.0.*,!=6.1,!=6.1.1,<7.0,>=4.1", - "docopt>=0.6.1", - "requests>=1.0.0", -] -files = [ - {file = "coveralls-3.3.1-py2.py3-none-any.whl", hash = "sha256:f42015f31d386b351d4226389b387ae173207058832fbf5c8ec4b40e27b16026"}, - {file = "coveralls-3.3.1.tar.gz", hash = "sha256:b32a8bb5d2df585207c119d6c01567b81fba690c9c10a753bfe27a335bfc43ea"}, -] - [[package]] name = "distlib" version = "0.3.8" @@ -393,15 +379,6 @@ files = [ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] -[[package]] -name = "docopt" -version = "0.6.2" -summary = "Pythonic argument parser, that will make you smile" -groups = ["ci"] -files = [ - {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, -] - [[package]] name = "docutils" version = "0.19" @@ -471,7 +448,8 @@ name = "idna" version = "3.8" requires_python = ">=3.6" summary = "Internationalized Domain Names in Applications (IDNA)" -groups = ["ci", "docs"] +groups = ["docs"] +marker = "python_version >= \"3.9\"" files = [ {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, @@ -858,7 +836,8 @@ name = "requests" version = "2.32.3" requires_python = ">=3.8" summary = "Python HTTP for Humans." -groups = ["ci", "docs"] +groups = ["docs"] +marker = "python_version >= \"3.9\"" dependencies = [ "certifi>=2017.4.17", "charset-normalizer<4,>=2", @@ -1165,7 +1144,8 @@ name = "urllib3" version = "2.2.2" requires_python = ">=3.8" summary = "HTTP library with thread-safe connection pooling, file post, and more." -groups = ["ci", "docs"] +groups = ["docs"] +marker = "python_version >= \"3.9\"" files = [ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, diff --git a/pyproject.toml b/pyproject.toml index 562ca99..ccda2b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,7 +86,6 @@ dev = [ ci = [ "tox<5,>=4", "tox-gh-actions", - "coveralls", "tomli; python_version < \"3.12\"", ] test = [ @@ -204,11 +203,6 @@ norecursedirs = ".github getmac samples scripts .git .tox .vagrant .idea .vscode addopts = "--tb=short" -# --- Coverage configuration --- -[tool.coverage.run] -relative_files = true # required for coveralls - - # --- Codespell configuration --- [tool.codespell] check-filenames = true From aeb4c85f675ff29ff849defa814bd15ca1200ada Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 14:53:16 -0600 Subject: [PATCH 063/112] Remove tox --- .github/pull_request_template.md | 8 +- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 6 +- CONTRIBUTING.md | 61 ++++++++------- docs/TODO.md | 3 +- docs/releasing.md | 10 +-- pdm.lock | 127 ++----------------------------- pyproject.toml | 11 +-- tox.ini | 56 -------------- 9 files changed, 57 insertions(+), 227 deletions(-) delete mode 100644 tox.ini diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0ab7e30..4516752 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -8,12 +8,12 @@ ### Code changes: -- [ ] Did you format your code with Black? - - `black getmac tests` +- [ ] Did you format your code? + - `pdm run format` - [ ] Are the linting checks passing? - - `tox -e check` + - `pdm run lint` - [ ] Do all tests pass locally? - - `tox` + - `pdm run test` - [ ] Have you updated the [CHANGELOG](CHANGELOG.md) with a summary of your change? - [ ] Did you add your name to the contributors list in the [README](README.md)? diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ec85b1..ccfb692 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,7 +68,7 @@ jobs: python -c "import os; print('os.name: %s' % str(os.name))" python -c "import platform; print('platform.uname(): %s' % str(platform.uname()))" - name: Install dependencies - run: pdm sync -d -G test,ci + run: pdm sync -d -G test # TODO: fix benchmark results (maybe a separate Job?) - name: Run tests run: pdm run test diff --git a/CHANGELOG.md b/CHANGELOG.md index 438786c..fabfcb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,10 +32,10 @@ ### Dev * Renamed `master` branch to `main` -* Switch to [PDM](https://pdm-project.org) for project management +* Switched to [PDM](https://pdm-project.org) for project management * Add `pyproject.toml`, consolidated most tool configurations here - * Remove `setup.py`, `MANIFEST.in`, and `requirement.txt` files -* Switch to CodeCov from Coveralls + * Removed `setup.py`, `MANIFEST.in`, `requirements*.txt`, `tox.ini` +* Switched to CodeCov from Coveralls ## 0.9.5 (07/15/2024) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aad7f82..2b7c594 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,22 +8,32 @@ Thanks for taking an interest in this awesome little project. We love to bring n # Code requirements -* Must work under all supported Python versions (2.7 and 3.4+) -* Must work on all supported platforms (if applicable) -* Try to match the general code style (loosely PEP8) -* Be respectful. Memes, references, and jokes are ok. Explicit language (cursing/swearing), NSFW text/content, or racism are NOT ok. + +Your code *must*: +* Have tests +* Work with all supported Python versions +* Work on all supported platforms +* Pass linting +* Pass CI +* Adhere to the [Code of Conduct](CODE_OF_CONDUCT.md) + +Most of these requirements are checked in CI (GitHub Actions), including Python versions and most supported platforms. Code is formatted with [Black](https://github.com/psf/black). You can write whatever format you want, as long as you run Black (`pdm run format`) before pushing, you're good. + +Please be respectful and follow the [Code of Conduct](CODE_OF_CONDUCT.md). Memes, references, and jokes are OK. Be nice, we're all human, and code is the great equalizer. ## Checklist before submitting a pull request -* [ ] Code is formatted using `black` (`black getmac tests`) +* [ ] Code is formatted using `black` (`pdm run format`) * [ ] All tests run and pass locally - * [ ] `poetry run tox` - * [ ] `poetry run tox -e check` -* [ ] Update the [CHANGELOG](CHANGELOG.md) (For non-trivial changes, e.g. changing functionality or adding tests) -* [ ] Add your name to the contributors list in the [README](README.md) + * [ ] Tests: `pdm run test` + * [ ] Benchmarks: `pdm run benchmark` + * [ ] Lint: `pdm run lint` +* [ ] Update the [CHANGELOG](CHANGELOG.md) (if applicable, for non-trivial changes) +* [ ] Add your name to the contributors list in the [README](README.md) (please include what your contribution was after your name) ## Checklist before a Pull Request will be merged * [ ] *All* tests pass in GitHub Actions * [ ] Code has been reviewed by at least one maintainer +* [ ] Coverage has NOT decreased # Where to contribute @@ -62,22 +72,22 @@ Examples of output of various commands is an easy way contribute that is still i 1. Create your own fork of the code through GitHub web interface ([Here's a Guide](https://gist.github.com/Chaser324/ce0505fbed06b947d962)) 1. Clone the fork to your computer. This can be done using the [GitHub desktop](https://desktop.github.com/) GUI , `git clone `, or the Git tools in your favorite editor or IDE. 1. Create and checkout a new branch in the fork with either your username (e.g. "ghostofgoes"), or the name of the feature or issue you're working on (e.g. "openbsd-support"). Again, this can be done using the GUI, your favorite editor, or `git checkout -b origin/`. -1. Install Poetry: https://python-poetry.org/docs/ -1. Create local environment: +2. Install PDM: https://pdm-project.org/en/latest/#installation +3. Create local environment: ```bash - poetry install + pdm install -d ``` -1. Ensure tests work locally: +4. Ensure tests work locally: ```bash # Run code quality checks - poetry run tox -e check + pdm run lint # Run the tests - poetry run tox + pdm run test ``` -1. Write some code! Git commit messages should information about what changed, and if it's relevant, the rationale (thinking) for the change. -1. Follow the checklist in the pull request template -1. Submit a pull request! +5. Write some code! Git commit messages should information about what changed, and if it's relevant, the rationale (thinking) for the change. +6. Follow the checklist in the pull request template +7. Submit a pull request! # Bug reports @@ -113,20 +123,17 @@ Ideas for features or other things are welcomed. Open an issue on GitHub detaili # Commands ```bash # Create development environment -poetry install +pdm install -d -# Update poetry.lock -poetry update +# List scripts, these can be run with "pdm run" +pdm run -l # Run tests -poetry run tox +pdm run test # Lint checks -poetry run tox -e check - -# Run specific python tests (in this example, python 3.9) -poetry run tox -e py39 +pdm run lint # Run getmac CLI -poetry run getmac +pdm run getmac ``` diff --git a/docs/TODO.md b/docs/TODO.md index d702ca6..b0a0a57 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -63,7 +63,7 @@ ## Before releasing - [ ] Add a deprecation warning to `get-mac` package, don't publish it for 1.0.0 - [ ] Update supported versions table in [SECURITY.md](../SECURITY.md) -- [ ] Automate publishing to PyPI in GitHub Actions? +- [ ] Automate publishing to PyPI+GitHub Release in GitHub Actions - [ ] Edit README.md to append CHANGELOG before publishing to PyPI ## Notes @@ -81,7 +81,6 @@ Removing Tox, maybe. Can do version enumeration in GitHub Actions, for all platf - [x] add inline type annotations for method arguments. remove types from docstrings? - [x] Remove `shutilwhich.py` and `.coveragerc` - [x] Replace `flake8-mypy` with proper execution of mypy in tests (the project is dead and archived, https://github.com/ambv/flake8-mypy) -- [x] Switch to Poetry for project management - [x] Support Python 3.10 and 3.11 - [x] Update pytest (pytest 4, which we were using to support python 2.7, doesn't work with python 3.10) - [x] add tests + setup.py classifier diff --git a/docs/releasing.md b/docs/releasing.md index 825ed9d..c13a557 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -1,11 +1,11 @@ ## Requirements -- Poetry: https://python-poetry.org/docs/#installing-with-the-official-installer +- PDM: https://pdm-project.org/en/latest/#installation - Configured `~/.pypirc` file with a token for `getmac` (for publishing to PyPI) ## Cutting a release -1. Increment version number in `getmac/getmac.py` +1. Increment version number in `getmac/getmac.py` (in `__version__`) 1. Update CHANGELOG header from UNRELEASED to the version and add the date -1. Run static analysis checks (`tox -e check`) +1. Run static analysis checks (`pdm run lint`) 1. Ensure CI ([GitHub Actions](https://github.com/GhostofGoes/getmac/actions)) is passing on all checks and on all platforms 1. Ensure a pip install from source works on the main platforms: ```bash @@ -14,11 +14,11 @@ pip install https://github.com/ghostofgoes/getmac/archive/main.tar.gz 1. Clean the environment: `bash ./scripts/clean.sh` 1. Build the sdist (`.tar.gz`) and wheel (`.whl`) ```bash -poetry build +pdm build ``` 1. Upload the sdist (`.tar.gz`) and wheel (`.whl`) to PyPI ```bash -poetry publish +pdm publish ``` 1. Create a tagged release on GitHub including: a) The relevant section of the CHANGELOG in the body diff --git a/pdm.lock b/pdm.lock index c0c7a47..8d1a5ee 100644 --- a/pdm.lock +++ b/pdm.lock @@ -2,10 +2,10 @@ # It is not intended for manual editing. [metadata] -groups = ["default", "ci", "dev", "docs", "lint", "test"] +groups = ["default", "docs", "lint", "test"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:530dcdba010d4d7600ca88a7ba75d8d971fa187fd7f26ce6ecd7f1b5f0827b20" +content_hash = "sha256:6a3e317605acee0e758af573b6bbaf9f93490f1ac59af17dd6c84b58c914dd58" [[metadata.targets]] requires_python = "~=3.8" @@ -92,17 +92,6 @@ files = [ {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, ] -[[package]] -name = "cachetools" -version = "5.5.0" -requires_python = ">=3.7" -summary = "Extensible memoizing collections and decorators" -groups = ["ci", "dev"] -files = [ - {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, - {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, -] - [[package]] name = "certifi" version = "2024.7.4" @@ -115,17 +104,6 @@ files = [ {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] -[[package]] -name = "chardet" -version = "5.2.0" -requires_python = ">=3.7" -summary = "Universal encoding detector for Python 3" -groups = ["ci", "dev"] -files = [ - {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, - {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, -] - [[package]] name = "charset-normalizer" version = "3.3.2" @@ -244,7 +222,8 @@ name = "colorama" version = "0.4.6" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Cross-platform colored terminal text." -groups = ["ci", "dev", "docs", "lint", "test"] +groups = ["docs", "lint", "test"] +marker = "sys_platform == \"win32\" or platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -369,16 +348,6 @@ files = [ {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, ] -[[package]] -name = "distlib" -version = "0.3.8" -summary = "Distribution utilities" -groups = ["ci", "dev"] -files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, -] - [[package]] name = "docutils" version = "0.19" @@ -414,17 +383,6 @@ files = [ {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, ] -[[package]] -name = "filelock" -version = "3.15.4" -requires_python = ">=3.8" -summary = "A platform independent file lock." -groups = ["ci", "dev"] -files = [ - {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, - {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, -] - [[package]] name = "furo" version = "2024.8.6" @@ -627,7 +585,7 @@ name = "packaging" version = "24.1" requires_python = ">=3.8" summary = "Core utilities for Python packages" -groups = ["ci", "dev", "docs", "lint", "test"] +groups = ["docs", "lint", "test"] files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, @@ -649,7 +607,7 @@ name = "platformdirs" version = "4.2.2" requires_python = ">=3.8" summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -groups = ["ci", "dev", "lint"] +groups = ["lint"] files = [ {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, @@ -660,7 +618,7 @@ name = "pluggy" version = "1.5.0" requires_python = ">=3.8" summary = "plugin and hook calling mechanisms for python" -groups = ["ci", "dev", "test"] +groups = ["test"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -704,21 +662,6 @@ files = [ {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, ] -[[package]] -name = "pyproject-api" -version = "1.7.1" -requires_python = ">=3.8" -summary = "API to interact with the python pyproject.toml based projects" -groups = ["ci", "dev"] -dependencies = [ - "packaging>=24.1", - "tomli>=2.0.1; python_version < \"3.11\"", -] -files = [ - {file = "pyproject_api-1.7.1-py3-none-any.whl", hash = "sha256:2dc1654062c2b27733d8fd4cdda672b22fe8741ef1dde8e3a998a9547b071eeb"}, - {file = "pyproject_api-1.7.1.tar.gz", hash = "sha256:7ebc6cd10710f89f4cf2a2731710a98abce37ebff19427116ff2174c9236a827"}, -] - [[package]] name = "pytest" version = "7.4.4" @@ -1084,50 +1027,13 @@ name = "tomli" version = "2.0.1" requires_python = ">=3.7" summary = "A lil' TOML parser" -groups = ["ci", "dev", "lint", "test"] +groups = ["lint", "test"] marker = "python_version < \"3.12\"" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "tox" -version = "4.18.0" -requires_python = ">=3.8" -summary = "tox is a generic virtualenv management and test command line tool" -groups = ["ci", "dev"] -dependencies = [ - "cachetools>=5.4", - "chardet>=5.2", - "colorama>=0.4.6", - "filelock>=3.15.4", - "packaging>=24.1", - "platformdirs>=4.2.2", - "pluggy>=1.5", - "pyproject-api>=1.7.1", - "tomli>=2.0.1; python_version < \"3.11\"", - "virtualenv>=20.26.3", -] -files = [ - {file = "tox-4.18.0-py3-none-any.whl", hash = "sha256:0a457400cf70615dc0627eb70d293e80cd95d8ce174bb40ac011011f0c03a249"}, - {file = "tox-4.18.0.tar.gz", hash = "sha256:5dfa1cab9f146becd6e351333a82f9e0ade374451630ba65ee54584624c27b58"}, -] - -[[package]] -name = "tox-gh-actions" -version = "3.2.0" -requires_python = ">=3.7" -summary = "Seamless integration of tox into GitHub Actions" -groups = ["ci"] -dependencies = [ - "tox<5,>=4", -] -files = [ - {file = "tox-gh-actions-3.2.0.tar.gz", hash = "sha256:ac6fa3b8da51bc90dd77985fd55f09e746c6558c55910c0a93d643045a2b0ccc"}, - {file = "tox_gh_actions-3.2.0-py2.py3-none-any.whl", hash = "sha256:821b66a4751a788fa3e9617bd796d696507b08c6e1d929ee4faefba06b73b694"}, -] - [[package]] name = "typing-extensions" version = "4.12.2" @@ -1151,23 +1057,6 @@ files = [ {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] -[[package]] -name = "virtualenv" -version = "20.26.3" -requires_python = ">=3.7" -summary = "Virtual Python Environment builder" -groups = ["ci", "dev"] -dependencies = [ - "distlib<1,>=0.3.7", - "filelock<4,>=3.12.2", - "importlib-metadata>=6.6; python_version < \"3.8\"", - "platformdirs<5,>=3.9.1", -] -files = [ - {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, - {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, -] - [[package]] name = "vulture" version = "2.11" diff --git a/pyproject.toml b/pyproject.toml index ccda2b3..c343695 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "pdm.backend" [project] name = "getmac" dynamic = ["version"] # , "readme" -description = "Pure-Python package to get the MAC address of network interfaces and hosts on the local network" +description = "Platform-independent pure-Python module to get a MAC address" dependencies = [] readme = "README.md" # readme = { file = "README.md", content-type = "text/markdown" } @@ -80,14 +80,6 @@ getmac = "getmac.__main__:main" [tool.pdm.dev-dependencies] -dev = [ - "tox<5,>=4", -] -ci = [ - "tox<5,>=4", - "tox-gh-actions", - "tomli; python_version < \"3.12\"", -] test = [ "pytest<8,>=7", "pytest-randomly", @@ -124,7 +116,6 @@ source-includes = [ "CONTRIBUTING.md", "SECURITY.md", "pyproject.toml", - "tox.ini", ] [tool.pdm.version] diff --git a/tox.ini b/tox.ini deleted file mode 100644 index f3d0dc9..0000000 --- a/tox.ini +++ /dev/null @@ -1,56 +0,0 @@ -[tox] -envlist = py{38,39,310,311,312}-{linux,macos,windows} -skip_missing_interpreters = true - -[gh-actions] -python = - 3.8: py38 - 3.9: py39 - 3.10: py310 - 3.11: py311 - 3.12: py312 - -[gh-actions:env] -PLATFORM = - ubuntu-20.04: linux - ubuntu-latest: linux - macos-13: macos - macos-latest: macos - windows-latest: windows - -[testenv] -setenv = - PIP_DISABLE_PIP_VERSION_CHECK = 1 -passenv = - SSL_CERT_FILE - TOXENV - CI - CODECOV_* -platform = - linux: linux - macos: darwin - windows: win32 -allowlist_externals = poetry -commands_pre = - poetry install --no-root --sync --no-interaction --only=main,ci,lint,test --quiet -; TODO: --benchmark-enable? -commands = - poetry run pytest \ - --basetemp={envtmpdir} --benchmark-disable --cov=getmac \ - --cov-report term --cov-report html --cov-report xml \ - -n auto {posargs} - -[testenv:benchmark] -description = Run the test suite with benchmarks enabled -commands = - poetry run pytest --basetemp={envtmpdir} --benchmark-enable -n auto {posargs} - -[testenv:check] -description = Run code quality, static analysis, and spelling checks -basepython = py311, py310, py3 -commands = - poetry run codespell --toml {toxinidir}/pyproject.toml - poetry run ruff check getmac tests - poetry run vulture --min-confidence 100 getmac tests - poetry run black --check --quiet getmac tests - poetry run mypy getmac tests From 7603b119d27c130ed1c71ba7d8153fcb104974f0 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 23 Aug 2024 15:54:17 -0600 Subject: [PATCH 064/112] doc tools, SECURITY.md updates --- SECURITY.md | 6 ++++-- docs/Makefile | 29 ----------------------------- docs/TODO.md | 4 ++-- docs/conf.py | 9 +++++---- pdm.lock | 49 +++++++++++++++++++++++++++++++------------------ pyproject.toml | 9 +++++---- 6 files changed, 47 insertions(+), 59 deletions(-) delete mode 100644 docs/Makefile diff --git a/SECURITY.md b/SECURITY.md index 3c8974a..0b02f73 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,8 +4,10 @@ | Version | Supported | | ---------- | ------------------ | -| 0.8.x | :white_check_mark: | -| <= 0.7.x | :x: | +| 1.0.x | :white_check_mark: Yes | +| 0.9.x | :heavy_exclamation_mark: Maintenance mode, security or major bug fixes only | +| 0.8.x | :x: No longer supported | | +| <= 0.7.x | :x: No longer supported | ## Reporting a Vulnerability or other security issue diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index a1f1156..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile man clean - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -# Generate Unix manual page -man: - mkdir -p ./_build/man - $(SPHINXBUILD) -b man . ./_build/man - mv ./_build/man/getmac.1 . - rm -Rf ./_build/man - -clean: - rm -rf ./_build/* diff --git a/docs/TODO.md b/docs/TODO.md index b0a0a57..f76dad3 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -12,7 +12,7 @@ - [x] Document `initialize_method_cache()` - [ ] Auto-generated API docs - [x] Add docstrings to all util methods -- [ ] Furo, sphinx-autodoc-typehints, sphinx-argparse-cli, sphinx-automodapi, sphinx-copybutton, recommonmark +- [x] Furo, sphinx-autodoc-typehints, sphinx-argparse-cli, sphinx-automodapi, sphinx-copybutton, recommonmark ## Tests - [ ] Add test to ensure only the expected files make it into the sdist and wheel, no unexpected files @@ -62,7 +62,7 @@ ## Before releasing - [ ] Add a deprecation warning to `get-mac` package, don't publish it for 1.0.0 -- [ ] Update supported versions table in [SECURITY.md](../SECURITY.md) +- [x] Update supported versions table in [SECURITY.md](../SECURITY.md) - [ ] Automate publishing to PyPI+GitHub Release in GitHub Actions - [ ] Edit README.md to append CHANGELOG before publishing to PyPI diff --git a/docs/conf.py b/docs/conf.py index 0f6a93e..da216e0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -32,13 +32,14 @@ # -- General configuration --------------------------------------------------- -# TODO: sphinx-copybutton -# TODO: sphinx-tabs (or another extension) to provide tabbed command -# examples for different platforms (e.g. for developer docs and CLI usage) -# TODO(python3): sphinx-autodoc-typehints extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', + 'sphinx_autodoc_typehints', + 'sphinx_inline_tabs', + 'sphinx_copybutton', + 'sphinx_argparse_cli', + 'sphinx_automodapi.automodapi', ] # TODO: setup ReadTheDocs diff --git a/pdm.lock b/pdm.lock index 8d1a5ee..776f822 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "docs", "lint", "test"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:6a3e317605acee0e758af573b6bbaf9f93490f1ac59af17dd6c84b58c914dd58" +content_hash = "sha256:3566b05c118d1c7d67c5cb4ebf1289de02748b7031a2f20d5cd505b293efd47b" [[metadata.targets]] requires_python = "~=3.8" @@ -873,6 +873,21 @@ files = [ {file = "sphinx-6.2.1-py3-none-any.whl", hash = "sha256:97787ff1fa3256a3eef9eda523a63dbf299f7b47e053cfcf684a1c2a8380c912"}, ] +[[package]] +name = "sphinx-argparse-cli" +version = "1.11.0" +requires_python = ">=3.7" +summary = "render CLI arguments (sub-commands friendly) defined by argparse module" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "sphinx!=6.1,>=5.3", +] +files = [ + {file = "sphinx_argparse_cli-1.11.0-py3-none-any.whl", hash = "sha256:a0a2a153816487d2151c47f2d496ed714b040226a3dcd0f66ff823c6497ae3cd"}, + {file = "sphinx_argparse_cli-1.11.0.tar.gz", hash = "sha256:03e5012ea8456a29aa45ae4583c74b8be8733ea0a6f515e6beff4fb6a1242ec2"}, +] + [[package]] name = "sphinx-autodoc-typehints" version = "1.23.0" @@ -888,6 +903,21 @@ files = [ {file = "sphinx_autodoc_typehints-1.23.0.tar.gz", hash = "sha256:5d44e2996633cdada499b6d27a496ddf9dbc95dd1f0f09f7b37940249e61f6e9"}, ] +[[package]] +name = "sphinx-automodapi" +version = "0.17.0" +requires_python = ">=3.8" +summary = "Sphinx extension for auto-generating API documentation for entire modules" +groups = ["docs"] +marker = "python_version >= \"3.9\"" +dependencies = [ + "sphinx>=4", +] +files = [ + {file = "sphinx-automodapi-0.17.0.tar.gz", hash = "sha256:7ccdadad57add4aa9149d9f2bb5cf28c8f8b590280b4735b1156ea8355c423a1"}, + {file = "sphinx_automodapi-0.17.0-py3-none-any.whl", hash = "sha256:4d029cb79eef29413e94ab01bb0177ebd2d5ba86e9789b73575afe9c06ae1501"}, +] + [[package]] name = "sphinx-basic-ng" version = "1.0.0b2" @@ -933,23 +963,6 @@ files = [ {file = "sphinx_inline_tabs-2022.1.2b11.tar.gz", hash = "sha256:afb9142772ec05ccb07f05d8181b518188fc55631b26ee803c694e812b3fdd73"}, ] -[[package]] -name = "sphinx-tabs" -version = "3.4.5" -requires_python = "~=3.7" -summary = "Tabbed views for Sphinx" -groups = ["docs"] -marker = "python_version >= \"3.9\"" -dependencies = [ - "docutils", - "pygments", - "sphinx", -] -files = [ - {file = "sphinx-tabs-3.4.5.tar.gz", hash = "sha256:ba9d0c1e3e37aaadd4b5678449eb08176770e0fc227e769b6ce747df3ceea531"}, - {file = "sphinx_tabs-3.4.5-py3-none-any.whl", hash = "sha256:92cc9473e2ecf1828ca3f6617d0efc0aa8acb06b08c56ba29d1413f2f0f6cf09"}, -] - [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0" diff --git a/pyproject.toml b/pyproject.toml index c343695..fc088ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -102,10 +102,11 @@ docs = [ "furo; python_version >= \"3.9\"", "sphinx<7.0.0,>=6.1.3; python_version >= \"3.9\"", "recommonmark<1.0.0,>=0.7.1; python_version >= \"3.9\"", - "sphinx-autodoc-typehints<2.0.0,>=1.21.8; python_version >= \"3.9\"", - "sphinx-copybutton<1.0.0,>=0.5.1; python_version >= \"3.9\"", - "sphinx-inline-tabs<2023.0.0,>=2022.1.2b11; python_version >= \"3.9\"", - "sphinx-tabs<4.0.0,>=3.4.1; python_version >= \"3.9\"", + "sphinx-autodoc-typehints; python_version >= \"3.9\"", + "sphinx-copybutton; python_version >= \"3.9\"", + "sphinx-inline-tabs; python_version >= \"3.9\"", + "sphinx-argparse-cli; python_version >= \"3.9\"", + "sphinx-automodapi; python_version >= \"3.9\"", ] [tool.pdm.build] From 5c36a39139e257b4c6f4fc35fd53dbe4f9c47d90 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 24 Mar 2025 23:25:30 -0600 Subject: [PATCH 065/112] reorganize docs --- docs/{ => misc_docs}/TODO.md | 1 + docs/{ => misc_docs}/android.md | 0 docs/{ => misc_docs}/darwin.md | 0 docs/{ => misc_docs}/releasing.md | 0 docs/{ => misc_docs}/rewrite.md | 2 ++ 5 files changed, 3 insertions(+) rename docs/{ => misc_docs}/TODO.md (99%) rename docs/{ => misc_docs}/android.md (100%) rename docs/{ => misc_docs}/darwin.md (100%) rename docs/{ => misc_docs}/releasing.md (100%) rename docs/{ => misc_docs}/rewrite.md (97%) diff --git a/docs/TODO.md b/docs/misc_docs/TODO.md similarity index 99% rename from docs/TODO.md rename to docs/misc_docs/TODO.md index f76dad3..0ee9405 100644 --- a/docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -91,6 +91,7 @@ Removing Tox, maybe. Can do version enumeration in GitHub Actions, for all platf # Etc +- [ ] Add to Conda Forge ([example here](https://github.com/conda-forge/staged-recipes/pull/26828/files)) - [ ] Add [isort](https://pycqa.github.io/isort/) (requires python 3.8+) - [ ] cache the result of executable checks in `getmac.utils.popen()` - [ ] Refactor the default interface code. Combine the functions into diff --git a/docs/android.md b/docs/misc_docs/android.md similarity index 100% rename from docs/android.md rename to docs/misc_docs/android.md diff --git a/docs/darwin.md b/docs/misc_docs/darwin.md similarity index 100% rename from docs/darwin.md rename to docs/misc_docs/darwin.md diff --git a/docs/releasing.md b/docs/misc_docs/releasing.md similarity index 100% rename from docs/releasing.md rename to docs/misc_docs/releasing.md diff --git a/docs/rewrite.md b/docs/misc_docs/rewrite.md similarity index 97% rename from docs/rewrite.md rename to docs/misc_docs/rewrite.md index 8d3eed8..6df3270 100644 --- a/docs/rewrite.md +++ b/docs/misc_docs/rewrite.md @@ -1,5 +1,7 @@ # Rewrite summary +This is a summary of the rewrite of the core internals that was released with version `0.9.0`. + **NOTE**: The public API of `getmac` is **unchanged** as part of this rewrite. `get_mac_address()` is still the primary way of getting a MAC address, it's just the "under the hood" internals that have changed completely. ## Motivation From 8976ce29637a17e4c2c2c402ca1765fa4faf9a35 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 24 Mar 2025 23:25:57 -0600 Subject: [PATCH 066/112] WIP on docs --- docs/api.rst | 2 + docs/cli.rst | 115 ++++++++++++++++++++++++++++++++++++++++++++++ docs/examples.rst | 0 docs/index.rst | 38 +++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 docs/api.rst create mode 100644 docs/cli.rst create mode 100644 docs/examples.rst create mode 100644 docs/index.rst diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..139597f --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,2 @@ + + diff --git a/docs/cli.rst b/docs/cli.rst new file mode 100644 index 0000000..c2d9059 --- /dev/null +++ b/docs/cli.rst @@ -0,0 +1,115 @@ + +Basic Usage +=========== + +The ``getmac`` command is included when you install the package. This provides developers and power users (like you!) a cross-platform tool to get MAC addresses. + +.. note:: + Depending on your Python environment, you may be able to invoke it directly (``getmac``) or have to reference your Python interpreter to invoke it. See :ref:`python-invoke` for details. + +.. note:: + On Windows, ``getmac.exe`` is a system binary. Depending on the value of the ``PATH`` environment variable, it may get prioritized over the ``getmac`` shim installed by pip. Ensure the ``\Scripts\`` folder in site-packages is added to the PATH variable for your user! + + +.. code-block:: shell + + # Print usage and command line arguments + getmac --help + + # Print the current version + getmac --version + + # Invoking with no arguments will return MAC address of the default network interface + getmac + + # Interface names, IPv4/IPv6 addresses, or Hostnames can be specified + getmac --interface ens33 + getmac --ip 192.168.0.1 + getmac --ip6 ::1 + getmac --hostname home.router + + +Examples +======== + +Remote hosts +------------ + +Getting the MAC address of a remote host requires the ARP table to be populated. By default, getmac will populate the table by sending a UDP packet to a high port on the host (defaults to ``55555``). This can be disabled with ``--no-network-request``, as shown below. + +.. code-block:: shell + + getmac --no-network-request --ip 192.168.0.1 + getmac --no-network-request -n home.router + + +Enabling logging messages and debugging +--------------------------------------- + +.. note:: + + When reporting an issue or asking for help, please enable verbose and the highest level of debugging: ``getmac -v -dddd [arguments]`` + +Normally, no log messages are printed, just the result from the command. Adding ``-v`` (``--verbose``) argument to any command will enable these messages. This is useful for debugging issues or understanding what's happening. + +.. code-block:: shell + + getmac --verbose + getmac -v -i eth0 + getmac --verbose --ip 192.168.0.1 + + +There is also a debugging mode. There are multiple levels of debugging, up to 4. Adding ``-d`` (``--debug``) argument argument enables debugging, and additional ``-d`` arguments increase the level of debugging. This argument must be combined with ``-v`` (``--verbose``), otherwise it's useless. + +.. code-block:: shell + + # Enable debugging at level 1 + getmac -v -d + + # Example with another argument + getmac -v -d -i enp11s4 + + # Debug level 2 (note the two 'd' characters) + getmac -v -dd -n home.router + + # Debug level 3 + getmac -v -ddd --ip 192.168.0.1 + + # Debug level 4 + getmac -v -dddd -i eth0 + + + + +.. ref: python-invoke + +Usage by specifying Python interpreter +-------------------------------------- + +If ``getmac`` doesn't work, try ``python3 -m getmac``, where ``python3`` is whatever command you use to run Python and used to install the package. + +On Windows, this may be ``py``, e.g. ``py -m getmac``, or just ``python``, e.g. ``python -m getmac``. + +When invoking in this fashion, in the examples above you can simply replace calls to ``getmac`` with ``python3 -m getmac`` (or whatever invocation applies to your platform). + +.. code-block:: shell + + python3 -m getmac + python3 -m getmac --help + python3 -m getmac --version + + +Examples of running as a Python module with shorthands for the arguments + +.. code-block:: shell + + python3 -m getmac -i 'Ethernet 4' + python3 -m getmac -4 192.168.0.1 + python3 -m getmac -6 ::1 + python3 -m getmac -n home.router + + +Arguments +========= + + Put requisite calls to sphinx_argparse_cli here.=== diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..a37f2a8 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,38 @@ + + + + +Installation +============ + +Stable release from PyPI +------------------------ + +.. code-block:: shell + + pip install getmac + + +Latest development version +-------------------------- +This will install the current version from the main branch on GitHub. + +.. code-block:: shell + + pip install https://github.com/ghostofgoes/getmac/archive/main.tar.gz + + +Debian-based distributions +-------------------------- + +``apt`` may have it available, depending on your distro (see packaging status below): + +.. code-block:: shell + + sudo apt install getmac + + +OS Packaging Status +------------------- + +[![Packaging status](https://repology.org/badge/vertical-allrepos/getmac.svg)](https://repology.org/project/getmac/versions) From 251e005450065d3ad34b407d2f52a5fec481d69b Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 24 Mar 2025 23:26:07 -0600 Subject: [PATCH 067/112] add .rst to editorconfig --- .editorconfig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 7b3da08..3f8f2d1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -42,4 +42,9 @@ indent_size = 2 [**/tests/samples/**] indent_style = unset indent_size = unset -end_of_line = unset \ No newline at end of file +end_of_line = unset + +[*.rst] +indent_size = 3 +insert_final_newline = true +trim_trailing_whitespace = true From a08758b17d8957d459e4dbee870e6c5f32274f69 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 24 Mar 2025 23:26:26 -0600 Subject: [PATCH 068/112] update badges in readme --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7ec7327..8291ae2 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ -[![Latest version on PyPI](https://badge.fury.io/py/getmac.svg)](https://pypi.org/project/getmac/) +[![PyPI - Current Version](https://img.shields.io/pypi/v/getmac?label=PyPI)](https://pypi.org/project/getmac/) [![codecov](https://codecov.io/github/GhostofGoes/getmac/graph/badge.svg?token=5NxlAE9eGY)](https://codecov.io/github/GhostofGoes/getmac) [![GitHub Actions Pipeline Status](https://github.com/ghostofgoes/getmac/actions/workflows/ci.yml/badge.svg)](https://github.com/GhostofGoes/getmac/actions) +![Python Version](https://img.shields.io/badge/Python-3.8|3.9|3.10|3.11|3.12-blue.svg) +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9388/badge)](https://www.bestpractices.dev/projects/9388) [![Weekly PyPI downloads](https://pepy.tech/badge/getmac/week)](https://pepy.tech/project/getmac) [![PyPI downloads](https://pepy.tech/badge/getmac)](https://pepy.tech/project/getmac) -[![PyPI downloads of the old name](https://pepy.tech/badge/get-mac)](https://pepy.tech/project/get-mac) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) -[![pdm-managed](https://img.shields.io/endpoint?url=https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2Fpdm-project%2F.github%2Fbadge.json)](https://pdm-project.org) + +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![PDM-managed](https://img.shields.io/endpoint?url=https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2Fpdm-project%2F.github%2Fbadge.json)](https://pdm-project.org) +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) trackgit-views @@ -45,7 +48,7 @@ pip install https://github.com/ghostofgoes/getmac/archive/main.tar.gz ``` ### Debian-based distributions -This may have it available, depending on your distro (see packaging status below): +`apt` may have it available, depending on your distro (see packaging status below): ```bash sudo apt install getmac From e4f4c2606843dc2914810199b210f1a33c774bdd Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 24 Mar 2025 23:26:50 -0600 Subject: [PATCH 069/112] update deps i guess? (need to relock this is old) --- pdm.lock | 107 +++++++++++++++++++++++++------------------------ pyproject.toml | 16 ++++---- 2 files changed, 62 insertions(+), 61 deletions(-) diff --git a/pdm.lock b/pdm.lock index 776f822..234b2ba 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "docs", "lint", "test"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:3566b05c118d1c7d67c5cb4ebf1289de02748b7031a2f20d5cd505b293efd47b" +content_hash = "sha256:f37ade6ad1b09edf7912a772e273b2099d717510a26dd9b69b7b3de37d115b52" [[metadata.targets]] requires_python = "~=3.8" @@ -16,7 +16,7 @@ version = "0.7.16" requires_python = ">=3.9" summary = "A light, configurable Sphinx theme" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, @@ -28,7 +28,7 @@ version = "2.16.0" requires_python = ">=3.8" summary = "Internationalization utilities" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "pytz>=2015.7; python_version < \"3.9\"", ] @@ -43,7 +43,7 @@ version = "4.12.3" requires_python = ">=3.6.0" summary = "Screen-scraping library" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "soupsieve>1.2", ] @@ -98,7 +98,7 @@ version = "2024.7.4" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, @@ -110,7 +110,7 @@ version = "3.3.2" requires_python = ">=3.7.0" summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, @@ -234,7 +234,7 @@ name = "commonmark" version = "0.9.1" summary = "Python parser for the CommonMark Markdown spec" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "future>=0.14.0; python_version < \"3\"", ] @@ -350,14 +350,14 @@ files = [ [[package]] name = "docutils" -version = "0.19" -requires_python = ">=3.7" +version = "0.21.2" +requires_python = ">=3.9" summary = "Docutils -- Python Documentation Utilities" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ - {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, - {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, + {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, + {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, ] [[package]] @@ -389,7 +389,7 @@ version = "2024.8.6" requires_python = ">=3.8" summary = "A clean customisable Sphinx documentation theme." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "beautifulsoup4", "pygments>=2.7", @@ -407,7 +407,7 @@ version = "3.8" requires_python = ">=3.6" summary = "Internationalized Domain Names in Applications (IDNA)" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, @@ -419,7 +419,7 @@ version = "1.4.1" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" summary = "Getting image size from png/jpeg/jpeg2000/gif file" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, @@ -430,7 +430,7 @@ name = "importlib-metadata" version = "8.4.0" requires_python = ">=3.8" summary = "Read metadata from Python packages" -groups = ["docs", "test"] +groups = ["test"] marker = "python_version < \"3.10\"" dependencies = [ "typing-extensions>=3.6.4; python_version < \"3.8\"", @@ -458,7 +458,7 @@ version = "3.1.4" requires_python = ">=3.7" summary = "A very fast and expressive template engine." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "MarkupSafe>=2.0", ] @@ -473,7 +473,7 @@ version = "2.1.5" requires_python = ">=3.7" summary = "Safely add untrusted strings to HTML/XML markup." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, @@ -763,7 +763,7 @@ name = "recommonmark" version = "0.7.1" summary = "A docutils-compatibility bridge to CommonMark, enabling you to write CommonMark inside of Docutils & Sphinx projects." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "commonmark>=0.8.1", "docutils>=0.11", @@ -780,7 +780,7 @@ version = "2.32.3" requires_python = ">=3.8" summary = "Python HTTP for Humans." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "certifi>=2017.4.17", "charset-normalizer<4,>=2", @@ -824,7 +824,7 @@ name = "snowballstemmer" version = "2.2.0" summary = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, @@ -836,7 +836,7 @@ version = "2.6" requires_python = ">=3.8" summary = "A modern CSS selector implementation for Beautiful Soup." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, @@ -844,33 +844,34 @@ files = [ [[package]] name = "sphinx" -version = "6.2.1" -requires_python = ">=3.8" +version = "7.4.7" +requires_python = ">=3.9" summary = "Python documentation generator" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ - "Jinja2>=3.0", - "Pygments>=2.13", - "alabaster<0.8,>=0.7", - "babel>=2.9", - "colorama>=0.4.5; sys_platform == \"win32\"", - "docutils<0.20,>=0.18.1", + "Jinja2>=3.1", + "Pygments>=2.17", + "alabaster~=0.7.14", + "babel>=2.13", + "colorama>=0.4.6; sys_platform == \"win32\"", + "docutils<0.22,>=0.20", "imagesize>=1.3", - "importlib-metadata>=4.8; python_version < \"3.10\"", - "packaging>=21.0", - "requests>=2.25.0", - "snowballstemmer>=2.0", + "importlib-metadata>=6.0; python_version < \"3.10\"", + "packaging>=23.0", + "requests>=2.30.0", + "snowballstemmer>=2.2", "sphinxcontrib-applehelp", "sphinxcontrib-devhelp", "sphinxcontrib-htmlhelp>=2.0.0", "sphinxcontrib-jsmath", "sphinxcontrib-qthelp", - "sphinxcontrib-serializinghtml>=1.1.5", + "sphinxcontrib-serializinghtml>=1.1.9", + "tomli>=2; python_version < \"3.11\"", ] files = [ - {file = "Sphinx-6.2.1.tar.gz", hash = "sha256:6d56a34697bb749ffa0152feafc4b19836c755d90a7c59b72bc7dfd371b9cc6b"}, - {file = "sphinx-6.2.1-py3-none-any.whl", hash = "sha256:97787ff1fa3256a3eef9eda523a63dbf299f7b47e053cfcf684a1c2a8380c912"}, + {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"}, + {file = "sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe"}, ] [[package]] @@ -879,7 +880,7 @@ version = "1.11.0" requires_python = ">=3.7" summary = "render CLI arguments (sub-commands friendly) defined by argparse module" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "sphinx!=6.1,>=5.3", ] @@ -894,7 +895,7 @@ version = "1.23.0" requires_python = ">=3.7" summary = "Type hints (PEP 484) support for the Sphinx autodoc extension" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "sphinx>=5.3", ] @@ -909,7 +910,7 @@ version = "0.17.0" requires_python = ">=3.8" summary = "Sphinx extension for auto-generating API documentation for entire modules" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "sphinx>=4", ] @@ -924,7 +925,7 @@ version = "1.0.0b2" requires_python = ">=3.7" summary = "A modern skeleton for Sphinx themes." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "sphinx>=4.0", ] @@ -939,7 +940,7 @@ version = "0.5.2" requires_python = ">=3.7" summary = "Add a copy button to each of your code cells." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "sphinx>=1.8", ] @@ -954,7 +955,7 @@ version = "2022.1.2b11" requires_python = ">=3.8" summary = "Add inline tabbed content to your Sphinx documentation." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" dependencies = [ "sphinx>=3", ] @@ -969,7 +970,7 @@ version = "2.0.0" requires_python = ">=3.9" summary = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, @@ -981,7 +982,7 @@ version = "2.0.0" requires_python = ">=3.9" summary = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, @@ -993,7 +994,7 @@ version = "2.1.0" requires_python = ">=3.9" summary = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, @@ -1005,7 +1006,7 @@ version = "1.0.1" requires_python = ">=3.5" summary = "A sphinx extension which renders display math in HTML via JavaScript" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, @@ -1017,7 +1018,7 @@ version = "2.0.0" requires_python = ">=3.9" summary = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, @@ -1029,7 +1030,7 @@ version = "2.0.0" requires_python = ">=3.9" summary = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, @@ -1040,7 +1041,7 @@ name = "tomli" version = "2.0.1" requires_python = ">=3.7" summary = "A lil' TOML parser" -groups = ["lint", "test"] +groups = ["docs", "lint", "test"] marker = "python_version < \"3.12\"" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, @@ -1064,7 +1065,7 @@ version = "2.2.2" requires_python = ">=3.8" summary = "HTTP library with thread-safe connection pooling, file post, and more." groups = ["docs"] -marker = "python_version >= \"3.9\"" +marker = "python_version >= \"3.10\"" files = [ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, @@ -1089,7 +1090,7 @@ name = "zipp" version = "3.20.0" requires_python = ">=3.8" summary = "Backport of pathlib-compatible object wrapper for zip files" -groups = ["docs", "test"] +groups = ["test"] marker = "python_version < \"3.10\"" files = [ {file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"}, diff --git a/pyproject.toml b/pyproject.toml index fc088ee..0aac505 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,14 +99,14 @@ lint = [ "mypy<2.0.0,>=1.3.0", ] docs = [ - "furo; python_version >= \"3.9\"", - "sphinx<7.0.0,>=6.1.3; python_version >= \"3.9\"", - "recommonmark<1.0.0,>=0.7.1; python_version >= \"3.9\"", - "sphinx-autodoc-typehints; python_version >= \"3.9\"", - "sphinx-copybutton; python_version >= \"3.9\"", - "sphinx-inline-tabs; python_version >= \"3.9\"", - "sphinx-argparse-cli; python_version >= \"3.9\"", - "sphinx-automodapi; python_version >= \"3.9\"", + "furo; python_version >= \"3.10\"", + "sphinx>7.0.0; python_version >= \"3.10\"", + "recommonmark<1.0.0,>=0.7.1; python_version >= \"3.10\"", + "sphinx-autodoc-typehints; python_version >= \"3.10\"", + "sphinx-copybutton; python_version >= \"3.10\"", + "sphinx-inline-tabs; python_version >= \"3.10\"", + "sphinx-argparse-cli; python_version >= \"3.10\"", + "sphinx-automodapi; python_version >= \"3.10\"", ] [tool.pdm.build] From be62fec330ad806e778ba63048f723099024ff06 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 12 Jul 2025 13:55:06 -0600 Subject: [PATCH 070/112] fix: mark constants as Final Python doesn't enforce these, but the type checker sure does :) --- getmac/getmac.py | 30 +++++++++++++++++++----------- getmac/variables.py | 2 +- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/getmac/getmac.py b/getmac/getmac.py index 9e63b9b..54b04e6 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -37,7 +37,7 @@ IPv6Network, ) from subprocess import CalledProcessError -from typing import Dict, List, Optional, Set, Tuple, Type, Union +from typing import Dict, Final, List, Optional, Set, Tuple, Type, Union from . import utils from .variables import settings, consts, gvars @@ -144,7 +144,8 @@ def get(self, arg: str) -> Optional[str]: class ArpFile(Method): platforms = {"linux"} method_type = "ip4" - _path: str = os.environ.get("ARP_PATH", "/proc/net/arp") + + _path: Final[str] = os.environ.get("ARP_PATH", "/proc/net/arp") def test(self) -> bool: return utils.check_path(self._path) @@ -188,7 +189,8 @@ def get(self, arg: str) -> Optional[str]: class ArpOpenbsd(Method): platforms = {"openbsd"} method_type = "ip" - _regex: str = r"[ ]+" + consts.MAC_RE_COLON + + _regex: Final[str] = r"[ ]+" + consts.MAC_RE_COLON def test(self) -> bool: return utils.check_command("arp") @@ -318,9 +320,10 @@ class ArpingHost(Method): platforms = {"linux", "darwin"} method_type = "ip4" network_request = True + _is_iputils: bool = True - _habets_args: str = "-r -C 1 -c 1" - _iputils_args: str = "-f -c 1" + _habets_args: Final[str] = "-r -C 1 -c 1" + _iputils_args: Final[str] = "-f -c 1" def test(self) -> bool: return utils.check_command("arping") @@ -443,7 +446,8 @@ def get(self, arg: str) -> Optional[str]: class SysIfaceFile(Method): platforms = {"linux", "wsl"} method_type = "iface" - _path: str = "/sys/class/net/" + + _path: Final[str] = "/sys/class/net/" def test(self) -> bool: # Imperfect, but should work well enough @@ -517,7 +521,8 @@ class GetmacExe(Method): platforms = {"windows"} method_type = "iface" - _regexes: List[Tuple[str, str]] = [ + + _regexes: Final[List[Tuple[str, str]]] = [ # Connection Name (r"\r\n", r".*" + consts.MAC_RE_DASH + r".*\r\n"), # Network Adapter (the human-readable name) @@ -566,7 +571,8 @@ class IpconfigExe(Method): platforms = {"windows"} method_type = "iface" - _regex: str = ( + + _regex: Final[str] = ( r"(?:\n?[^\n]*){1,8}Physical Address[ .:]+" + consts.MAC_RE_DASH + r"\r\n" ) @@ -638,7 +644,7 @@ def get(self, arg: str) -> Optional[str]: # output from "ifconfig", and probably netstat too. It can probably be made more # efficient by someone who actually knows how to write regex. # [: ]\s?(?:flags=|\s).*?(?:(?:\w+[: ]\s?flags=)|\s(?:ether|address|HWaddr|hwaddr|lladdr)[ :]?\s?([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})) # noqa: E501 -IFCONFIG_REGEX = ( +IFCONFIG_REGEX: Final[str] = ( r"[: ]\s?(?:flags=|\s).*?(?:" r"(?:\w+[: ]\s?flags=)|" # Prevent interfaces w/o a MAC from matching r"\s(?:ether|address|HWaddr|hwaddr|lladdr)[ :]?\s?" # Handle various prefixes @@ -689,6 +695,7 @@ def get(self, arg: str) -> Optional[str]: class IfconfigEther(Method): platforms = {"darwin"} method_type = "iface" + _tested_arg: bool = False _iface_arg: bool = False @@ -791,7 +798,7 @@ class NetstatIface(Method): # ".*?": non-greedy # https://docs.python.org/3/howto/regex.html#greedy-versus-non-greedy - _regexes: List[str] = [ + _regexes: Final[List[str]] = [ r": .*?ether " + consts.MAC_RE_COLON, r": .*?HWaddr " + consts.MAC_RE_COLON, # Ubuntu 12.04 and other older kernels @@ -846,7 +853,8 @@ def get(self, arg: str) -> Optional[str]: class IpLinkIface(Method): platforms = {"linux", "wsl", "android", "other"} method_type = "iface" - _regex: str = r".*\n.*link/ether " + consts.MAC_RE_COLON + + _regex: Final[str] = r".*\n.*link/ether " + consts.MAC_RE_COLON _tested_arg: bool = False _iface_arg: bool = False diff --git a/getmac/variables.py b/getmac/variables.py index f12d1c6..6a94a20 100644 --- a/getmac/variables.py +++ b/getmac/variables.py @@ -2,7 +2,7 @@ import os import platform import sys -from typing import Dict, List +from typing import Dict, Final, List class VarsClass: From 8282b32c2ba9908682937c7525ffc8b8fa041bae Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 12 Jul 2025 13:55:37 -0600 Subject: [PATCH 071/112] doc: ignore built_docs --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 47b3c9a..f44756f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ .vagrant/ .pdm-python +built_docs/ + .pytest_cache # Byte-compiled / optimized / DLL files From ce26d787b622953bc2be07ccc509a029c6806600 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 12 Jul 2025 13:55:51 -0600 Subject: [PATCH 072/112] style: trim markdown files --- .editorconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.editorconfig b/.editorconfig index 3f8f2d1..0ab935c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -48,3 +48,7 @@ end_of_line = unset indent_size = 3 insert_final_newline = true trim_trailing_whitespace = true + +[*.md] +insert_final_newline = true +trim_trailing_whitespace = true From 3d47fa84bd8c04b19f35fe23124c293db825bce4 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 12 Jul 2025 13:57:29 -0600 Subject: [PATCH 073/112] deps: add sphinx-issues, update deps Also add a PDM script to build docs --- pdm.lock | 912 ++++++++++++++++++++++++++++--------------------- pyproject.toml | 4 + 2 files changed, 528 insertions(+), 388 deletions(-) diff --git a/pdm.lock b/pdm.lock index 234b2ba..1c9bed2 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,26 +5,26 @@ groups = ["default", "docs", "lint", "test"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:f37ade6ad1b09edf7912a772e273b2099d717510a26dd9b69b7b3de37d115b52" +content_hash = "sha256:42c906fbb87a3193b72c041b2cedcd4a1e4971c5cb18eae496cf8b969510c5cc" [[metadata.targets]] requires_python = "~=3.8" [[package]] name = "alabaster" -version = "0.7.16" -requires_python = ">=3.9" +version = "1.0.0" +requires_python = ">=3.10" summary = "A light, configurable Sphinx theme" groups = ["docs"] marker = "python_version >= \"3.10\"" files = [ - {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, - {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, + {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, + {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, ] [[package]] name = "babel" -version = "2.16.0" +version = "2.17.0" requires_python = ">=3.8" summary = "Internationalization utilities" groups = ["docs"] @@ -33,23 +33,24 @@ dependencies = [ "pytz>=2015.7; python_version < \"3.9\"", ] files = [ - {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, - {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, + {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, + {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, ] [[package]] name = "beautifulsoup4" -version = "4.12.3" -requires_python = ">=3.6.0" +version = "4.13.4" +requires_python = ">=3.7.0" summary = "Screen-scraping library" groups = ["docs"] marker = "python_version >= \"3.10\"" dependencies = [ "soupsieve>1.2", + "typing-extensions>=4.0.0", ] files = [ - {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, - {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, + {file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}, + {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, ] [[package]] @@ -94,106 +95,109 @@ files = [ [[package]] name = "certifi" -version = "2024.7.4" -requires_python = ">=3.6" +version = "2025.7.9" +requires_python = ">=3.7" summary = "Python package for providing Mozilla's CA Bundle." groups = ["docs"] marker = "python_version >= \"3.10\"" files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, + {file = "certifi-2025.7.9-py3-none-any.whl", hash = "sha256:d842783a14f8fdd646895ac26f719a061408834473cfc10203f6a575beb15d39"}, + {file = "certifi-2025.7.9.tar.gz", hash = "sha256:c1d2ec05395148ee10cf672ffc28cd37ea0ab0d99f9cc74c43e588cbd111b079"}, ] [[package]] name = "charset-normalizer" -version = "3.3.2" -requires_python = ">=3.7.0" +version = "3.4.2" +requires_python = ">=3.7" summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." groups = ["docs"] marker = "python_version >= \"3.10\"" files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, + {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, + {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, ] [[package]] name = "click" -version = "8.1.7" +version = "8.1.8" requires_python = ">=3.7" summary = "Composable command line interface toolkit" groups = ["lint"] @@ -202,19 +206,19 @@ dependencies = [ "importlib-metadata; python_version < \"3.8\"", ] files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, ] [[package]] name = "codespell" -version = "2.3.0" +version = "2.4.1" requires_python = ">=3.8" -summary = "Codespell" +summary = "Fix common misspellings in text files" groups = ["lint"] files = [ - {file = "codespell-2.3.0-py3-none-any.whl", hash = "sha256:a9c7cef2501c9cfede2110fd6d4e5e62296920efe9abfb84648df866e47f58d1"}, - {file = "codespell-2.3.0.tar.gz", hash = "sha256:360c7d10f75e65f67bad720af7007e1060a5d395670ec11a7ed1fed9dd17471f"}, + {file = "codespell-2.4.1-py3-none-any.whl", hash = "sha256:3dadafa67df7e4a3dbf51e0d7315061b80d265f9552ebd699b3dd6834b47e425"}, + {file = "codespell-2.4.1.tar.gz", hash = "sha256:299fcdcb09d23e81e35a671bbe746d5ad7e8385972e65dbb833a2eaac33c01e5"}, ] [[package]] @@ -245,107 +249,169 @@ files = [ [[package]] name = "coverage" -version = "6.5.0" -requires_python = ">=3.7" +version = "7.6.1" +requires_python = ">=3.8" summary = "Code coverage measurement for Python" groups = ["test"] files = [ - {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, - {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, - {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, - {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, - {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, - {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, - {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, - {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, - {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, - {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, - {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, - {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, - {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, + {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, + {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, + {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, + {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, + {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, + {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, + {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, + {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, + {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, + {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, + {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, + {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, + {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, + {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, + {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, + {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] [[package]] name = "coverage" -version = "6.5.0" +version = "7.6.1" extras = ["toml"] -requires_python = ">=3.7" +requires_python = ">=3.8" summary = "Code coverage measurement for Python" groups = ["test"] dependencies = [ - "coverage==6.5.0", + "coverage==7.6.1", "tomli; python_full_version <= \"3.11.0a6\"", ] files = [ - {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, - {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"}, - {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"}, - {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"}, - {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"}, - {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"}, - {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"}, - {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"}, - {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"}, - {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"}, - {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"}, - {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"}, - {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"}, - {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"}, - {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"}, - {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"}, - {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"}, - {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"}, - {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"}, - {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"}, - {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"}, - {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, - {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, + {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, + {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, + {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, + {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, + {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, + {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, + {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, + {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, + {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, + {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, + {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, + {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, + {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, + {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, + {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, + {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] [[package]] @@ -362,14 +428,17 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.2.2" +version = "1.3.0" requires_python = ">=3.7" summary = "Backport of PEP 654 (exception groups)" groups = ["test"] marker = "python_version < \"3.11\"" +dependencies = [ + "typing-extensions>=4.6.0; python_version < \"3.13\"", +] files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, + {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, + {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, ] [[package]] @@ -403,14 +472,14 @@ files = [ [[package]] name = "idna" -version = "3.8" +version = "3.10" requires_python = ">=3.6" summary = "Internationalized Domain Names in Applications (IDNA)" groups = ["docs"] marker = "python_version >= \"3.10\"" files = [ - {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, - {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] [[package]] @@ -427,34 +496,34 @@ files = [ [[package]] name = "importlib-metadata" -version = "8.4.0" +version = "8.5.0" requires_python = ">=3.8" summary = "Read metadata from Python packages" groups = ["test"] marker = "python_version < \"3.10\"" dependencies = [ "typing-extensions>=3.6.4; python_version < \"3.8\"", - "zipp>=0.5", + "zipp>=3.20", ] files = [ - {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, - {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, ] [[package]] name = "iniconfig" -version = "2.0.0" -requires_python = ">=3.7" +version = "2.1.0" +requires_python = ">=3.8" summary = "brain-dead simple config-ini parsing" groups = ["test"] files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] [[package]] name = "jinja2" -version = "3.1.4" +version = "3.1.6" requires_python = ">=3.7" summary = "A very fast and expressive template engine." groups = ["docs"] @@ -463,74 +532,84 @@ dependencies = [ "MarkupSafe>=2.0", ] files = [ - {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, - {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] [[package]] name = "markupsafe" -version = "2.1.5" -requires_python = ">=3.7" +version = "3.0.2" +requires_python = ">=3.9" summary = "Safely add untrusted strings to HTML/XML markup." groups = ["docs"] marker = "python_version >= \"3.10\"" files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] [[package]] name = "mypy" -version = "1.11.1" +version = "1.14.1" requires_python = ">=3.8" summary = "Optional static typing for Python" groups = ["lint"] @@ -540,55 +619,66 @@ dependencies = [ "typing-extensions>=4.6.0", ] files = [ - {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, - {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, - {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, - {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, - {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, - {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, - {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, - {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, - {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, - {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, - {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, - {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, - {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, - {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, - {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, - {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, - {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, - {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, - {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, + {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, + {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, + {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, + {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, + {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, + {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, + {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, + {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, + {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, + {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, + {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, + {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, + {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, + {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, + {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, + {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, + {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, + {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, + {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, + {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, + {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, + {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, + {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, + {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, + {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, + {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, + {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, + {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, + {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, + {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, + {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, + {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, + {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, + {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, + {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, + {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, + {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, + {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, ] [[package]] name = "mypy-extensions" -version = "1.0.0" -requires_python = ">=3.5" +version = "1.1.0" +requires_python = ">=3.8" summary = "Type system extensions for programs checked with the mypy type checker." groups = ["lint"] files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] [[package]] name = "packaging" -version = "24.1" +version = "25.0" requires_python = ">=3.8" summary = "Core utilities for Python packages" groups = ["docs", "lint", "test"] files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] [[package]] @@ -604,13 +694,13 @@ files = [ [[package]] name = "platformdirs" -version = "4.2.2" +version = "4.3.6" requires_python = ">=3.8" summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." groups = ["lint"] files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [[package]] @@ -626,19 +716,19 @@ files = [ [[package]] name = "psutil" -version = "6.0.0" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -summary = "Cross-platform lib for process and system monitoring in Python." +version = "7.0.0" +requires_python = ">=3.6" +summary = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7." groups = ["test"] files = [ - {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, - {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, - {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, - {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, - {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, + {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"}, + {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34"}, + {file = "psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993"}, + {file = "psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99"}, + {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"}, + {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"}, ] [[package]] @@ -653,13 +743,13 @@ files = [ [[package]] name = "pygments" -version = "2.18.0" +version = "2.19.2" requires_python = ">=3.8" summary = "Pygments is a syntax highlighting package written in Python." groups = ["docs", "test"] files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, ] [[package]] @@ -716,7 +806,7 @@ files = [ [[package]] name = "pytest-mock" -version = "3.14.0" +version = "3.14.1" requires_python = ">=3.8" summary = "Thin-wrapper around the mock package for easier use with pytest" groups = ["test"] @@ -724,8 +814,8 @@ dependencies = [ "pytest>=6.2.5", ] files = [ - {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, - {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, + {file = "pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0"}, + {file = "pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e"}, ] [[package]] @@ -776,7 +866,7 @@ files = [ [[package]] name = "requests" -version = "2.32.3" +version = "2.32.4" requires_python = ">=3.8" summary = "Python HTTP for Humans." groups = ["docs"] @@ -788,135 +878,136 @@ dependencies = [ "urllib3<3,>=1.21.1", ] files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, + {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, + {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, ] [[package]] name = "ruff" -version = "0.6.2" +version = "0.12.3" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["lint"] files = [ - {file = "ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c"}, - {file = "ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570"}, - {file = "ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1"}, - {file = "ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1"}, - {file = "ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23"}, - {file = "ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a"}, - {file = "ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c"}, - {file = "ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56"}, - {file = "ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da"}, - {file = "ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2"}, - {file = "ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9"}, - {file = "ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be"}, + {file = "ruff-0.12.3-py3-none-linux_armv6l.whl", hash = "sha256:47552138f7206454eaf0c4fe827e546e9ddac62c2a3d2585ca54d29a890137a2"}, + {file = "ruff-0.12.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0a9153b000c6fe169bb307f5bd1b691221c4286c133407b8827c406a55282041"}, + {file = "ruff-0.12.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fa6b24600cf3b750e48ddb6057e901dd5b9aa426e316addb2a1af185a7509882"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2506961bf6ead54887ba3562604d69cb430f59b42133d36976421bc8bd45901"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4faaff1f90cea9d3033cbbcdf1acf5d7fb11d8180758feb31337391691f3df0"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40dced4a79d7c264389de1c59467d5d5cefd79e7e06d1dfa2c75497b5269a5a6"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:0262d50ba2767ed0fe212aa7e62112a1dcbfd46b858c5bf7bbd11f326998bafc"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12371aec33e1a3758597c5c631bae9a5286f3c963bdfb4d17acdd2d395406687"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:560f13b6baa49785665276c963edc363f8ad4b4fc910a883e2625bdb14a83a9e"}, + {file = "ruff-0.12.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:023040a3499f6f974ae9091bcdd0385dd9e9eb4942f231c23c57708147b06311"}, + {file = "ruff-0.12.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:883d844967bffff5ab28bba1a4d246c1a1b2933f48cb9840f3fdc5111c603b07"}, + {file = "ruff-0.12.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2120d3aa855ff385e0e562fdee14d564c9675edbe41625c87eeab744a7830d12"}, + {file = "ruff-0.12.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6b16647cbb470eaf4750d27dddc6ebf7758b918887b56d39e9c22cce2049082b"}, + {file = "ruff-0.12.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e1417051edb436230023575b149e8ff843a324557fe0a265863b7602df86722f"}, + {file = "ruff-0.12.3-py3-none-win32.whl", hash = "sha256:dfd45e6e926deb6409d0616078a666ebce93e55e07f0fb0228d4b2608b2c248d"}, + {file = "ruff-0.12.3-py3-none-win_amd64.whl", hash = "sha256:a946cf1e7ba3209bdef039eb97647f1c77f6f540e5845ec9c114d3af8df873e7"}, + {file = "ruff-0.12.3-py3-none-win_arm64.whl", hash = "sha256:5f9c7c9c8f84c2d7f27e93674d27136fbf489720251544c4da7fb3d742e011b1"}, + {file = "ruff-0.12.3.tar.gz", hash = "sha256:f1b5a4b6668fd7b7ea3697d8d98857390b40c1320a63a178eee6be0899ea2d77"}, ] [[package]] name = "snowballstemmer" -version = "2.2.0" -summary = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +version = "3.0.1" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*" +summary = "This package provides 32 stemmers for 30 languages generated from Snowball algorithms." groups = ["docs"] marker = "python_version >= \"3.10\"" files = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, + {file = "snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064"}, + {file = "snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895"}, ] [[package]] name = "soupsieve" -version = "2.6" +version = "2.7" requires_python = ">=3.8" summary = "A modern CSS selector implementation for Beautiful Soup." groups = ["docs"] marker = "python_version >= \"3.10\"" files = [ - {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, - {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, + {file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, + {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, ] [[package]] name = "sphinx" -version = "7.4.7" -requires_python = ">=3.9" +version = "8.1.3" +requires_python = ">=3.10" summary = "Python documentation generator" groups = ["docs"] marker = "python_version >= \"3.10\"" dependencies = [ "Jinja2>=3.1", "Pygments>=2.17", - "alabaster~=0.7.14", + "alabaster>=0.7.14", "babel>=2.13", "colorama>=0.4.6; sys_platform == \"win32\"", "docutils<0.22,>=0.20", "imagesize>=1.3", - "importlib-metadata>=6.0; python_version < \"3.10\"", "packaging>=23.0", "requests>=2.30.0", "snowballstemmer>=2.2", - "sphinxcontrib-applehelp", - "sphinxcontrib-devhelp", - "sphinxcontrib-htmlhelp>=2.0.0", - "sphinxcontrib-jsmath", - "sphinxcontrib-qthelp", + "sphinxcontrib-applehelp>=1.0.7", + "sphinxcontrib-devhelp>=1.0.6", + "sphinxcontrib-htmlhelp>=2.0.6", + "sphinxcontrib-jsmath>=1.0.1", + "sphinxcontrib-qthelp>=1.0.6", "sphinxcontrib-serializinghtml>=1.1.9", "tomli>=2; python_version < \"3.11\"", ] files = [ - {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"}, - {file = "sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe"}, + {file = "sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2"}, + {file = "sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927"}, ] [[package]] name = "sphinx-argparse-cli" -version = "1.11.0" -requires_python = ">=3.7" +version = "1.19.0" +requires_python = ">=3.10" summary = "render CLI arguments (sub-commands friendly) defined by argparse module" groups = ["docs"] marker = "python_version >= \"3.10\"" dependencies = [ - "sphinx!=6.1,>=5.3", + "sphinx>=8.0.2", ] files = [ - {file = "sphinx_argparse_cli-1.11.0-py3-none-any.whl", hash = "sha256:a0a2a153816487d2151c47f2d496ed714b040226a3dcd0f66ff823c6497ae3cd"}, - {file = "sphinx_argparse_cli-1.11.0.tar.gz", hash = "sha256:03e5012ea8456a29aa45ae4583c74b8be8733ea0a6f515e6beff4fb6a1242ec2"}, + {file = "sphinx_argparse_cli-1.19.0-py3-none-any.whl", hash = "sha256:c0e069deed1db44d289f90469c04320f1c2249b3dcbd3c3e093bf9a66e4bd8e4"}, + {file = "sphinx_argparse_cli-1.19.0.tar.gz", hash = "sha256:0374b23560fd4246234c0ef2d8bb97703088c8ae721bf63a84b9fd0d32f87a78"}, ] [[package]] name = "sphinx-autodoc-typehints" -version = "1.23.0" -requires_python = ">=3.7" +version = "3.0.1" +requires_python = ">=3.10" summary = "Type hints (PEP 484) support for the Sphinx autodoc extension" groups = ["docs"] marker = "python_version >= \"3.10\"" dependencies = [ - "sphinx>=5.3", + "sphinx>=8.1.3", ] files = [ - {file = "sphinx_autodoc_typehints-1.23.0-py3-none-any.whl", hash = "sha256:ac099057e66b09e51b698058ba7dd76e57e1fe696cd91b54e121d3dad188f91d"}, - {file = "sphinx_autodoc_typehints-1.23.0.tar.gz", hash = "sha256:5d44e2996633cdada499b6d27a496ddf9dbc95dd1f0f09f7b37940249e61f6e9"}, + {file = "sphinx_autodoc_typehints-3.0.1-py3-none-any.whl", hash = "sha256:4b64b676a14b5b79cefb6628a6dc8070e320d4963e8ff640a2f3e9390ae9045a"}, + {file = "sphinx_autodoc_typehints-3.0.1.tar.gz", hash = "sha256:b9b40dd15dee54f6f810c924f863f9cf1c54f9f3265c495140ea01be7f44fa55"}, ] [[package]] name = "sphinx-automodapi" -version = "0.17.0" +version = "0.20.0" requires_python = ">=3.8" summary = "Sphinx extension for auto-generating API documentation for entire modules" groups = ["docs"] marker = "python_version >= \"3.10\"" dependencies = [ + "packaging", "sphinx>=4", ] files = [ - {file = "sphinx-automodapi-0.17.0.tar.gz", hash = "sha256:7ccdadad57add4aa9149d9f2bb5cf28c8f8b590280b4735b1156ea8355c423a1"}, - {file = "sphinx_automodapi-0.17.0-py3-none-any.whl", hash = "sha256:4d029cb79eef29413e94ab01bb0177ebd2d5ba86e9789b73575afe9c06ae1501"}, + {file = "sphinx_automodapi-0.20.0-py3-none-any.whl", hash = "sha256:1d205d4f07c22b1024b270a0340b245b5f819bc4c625ca10db8c06e437cf5d5e"}, + {file = "sphinx_automodapi-0.20.0.tar.gz", hash = "sha256:24cc97323f163aeefcefb9bdae9cd0a8ea7b699ece34feab116a2557a44949ac"}, ] [[package]] @@ -951,7 +1042,7 @@ files = [ [[package]] name = "sphinx-inline-tabs" -version = "2022.1.2b11" +version = "2023.4.21" requires_python = ">=3.8" summary = "Add inline tabbed content to your Sphinx documentation." groups = ["docs"] @@ -960,8 +1051,23 @@ dependencies = [ "sphinx>=3", ] files = [ - {file = "sphinx_inline_tabs-2022.1.2b11-py3-none-any.whl", hash = "sha256:bb4e807769ef52301a186d0678da719120b978a1af4fd62a1e9453684e962dbc"}, - {file = "sphinx_inline_tabs-2022.1.2b11.tar.gz", hash = "sha256:afb9142772ec05ccb07f05d8181b518188fc55631b26ee803c694e812b3fdd73"}, + {file = "sphinx_inline_tabs-2023.4.21-py3-none-any.whl", hash = "sha256:06809ac613f7c48ddd6e2fa588413e3fe92cff2397b56e2ccf0b0218f9ef6a78"}, + {file = "sphinx_inline_tabs-2023.4.21.tar.gz", hash = "sha256:5df2f13f602c158f3f5f6c509e008aeada199a8c76d97ba3aa2822206683bebc"}, +] + +[[package]] +name = "sphinx-issues" +version = "5.0.1" +requires_python = ">=3.9" +summary = "A Sphinx extension for linking to your project's issue tracker" +groups = ["docs"] +marker = "python_version >= \"3.10\"" +dependencies = [ + "sphinx", +] +files = [ + {file = "sphinx_issues-5.0.1-py3-none-any.whl", hash = "sha256:58cdd94df25546fa96a224afc9df17063deffaf38336c4a694d2bf478aec75a4"}, + {file = "sphinx_issues-5.0.1.tar.gz", hash = "sha256:6da131d4545af00be4b48ec7c4086ea82c1371a05116bbe5779f57cff34bf16a"}, ] [[package]] @@ -1038,42 +1144,72 @@ files = [ [[package]] name = "tomli" -version = "2.0.1" -requires_python = ">=3.7" +version = "2.2.1" +requires_python = ">=3.8" summary = "A lil' TOML parser" groups = ["docs", "lint", "test"] marker = "python_version < \"3.12\"" files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] name = "typing-extensions" -version = "4.12.2" +version = "4.13.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" -groups = ["lint"] +groups = ["docs", "lint", "test"] files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, + {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, + {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, ] [[package]] name = "urllib3" -version = "2.2.2" -requires_python = ">=3.8" +version = "2.5.0" +requires_python = ">=3.9" summary = "HTTP library with thread-safe connection pooling, file post, and more." groups = ["docs"] marker = "python_version >= \"3.10\"" files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [[package]] name = "vulture" -version = "2.11" +version = "2.14" requires_python = ">=3.8" summary = "Find dead code" groups = ["lint"] @@ -1081,18 +1217,18 @@ dependencies = [ "tomli>=1.1.0; python_version < \"3.11\"", ] files = [ - {file = "vulture-2.11-py2.py3-none-any.whl", hash = "sha256:12d745f7710ffbf6aeb8279ba9068a24d4e52e8ed333b8b044035c9d6b823aba"}, - {file = "vulture-2.11.tar.gz", hash = "sha256:f0fbb60bce6511aad87ee0736c502456737490a82d919a44e6d92262cb35f1c2"}, + {file = "vulture-2.14-py2.py3-none-any.whl", hash = "sha256:d9a90dba89607489548a49d557f8bac8112bd25d3cbc8aeef23e860811bd5ed9"}, + {file = "vulture-2.14.tar.gz", hash = "sha256:cb8277902a1138deeab796ec5bef7076a6e0248ca3607a3f3dee0b6d9e9b8415"}, ] [[package]] name = "zipp" -version = "3.20.0" +version = "3.20.2" requires_python = ">=3.8" summary = "Backport of pathlib-compatible object wrapper for zip files" groups = ["test"] marker = "python_version < \"3.10\"" files = [ - {file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"}, - {file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"}, + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, ] diff --git a/pyproject.toml b/pyproject.toml index 0aac505..46d07f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,6 +107,7 @@ docs = [ "sphinx-inline-tabs; python_version >= \"3.10\"", "sphinx-argparse-cli; python_version >= \"3.10\"", "sphinx-automodapi; python_version >= \"3.10\"", + "sphinx-issues; python_version >= \"3.10\"", ] [tool.pdm.build] @@ -187,6 +188,9 @@ pytest -n auto """ +[tool.pdm.scripts.docs] +cmd = "sphinx-build ./docs ./built_docs" + # --- Pytest configuration --- [tool.pytest.ini_options] From 15cf6ed9899cc726a9d51d7d7eae8fb62cd17c4f Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 12 Jul 2025 14:00:22 -0600 Subject: [PATCH 074/112] doc: refactor doc build - Furo theme - Enable more sphinx extensions - cleanup conf.py - use RST instead of markdown --- .gitignore | 1 + docs/conf.py | 106 ++++++++++------------------------------------ docs/examples.rst | 0 3 files changed, 23 insertions(+), 84 deletions(-) delete mode 100644 docs/examples.rst diff --git a/.gitignore b/.gitignore index f44756f..9a30972 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .pdm-python built_docs/ +docs/api/ .pytest_cache diff --git a/docs/conf.py b/docs/conf.py index da216e0..a3a6314 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,24 +1,8 @@ -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -import os -import sys -from datetime import datetime -DOCDIR = os.path.pardir(os.path.abspath(__file__)) -sys.path.insert(0, os.path.join(os.path.pardir(DOCDIR), 'getmac')) +"""Sphinx configuration file for getmac's documentation.""" -from recommonmark.transform import AutoStructify # noqa: E402 +from datetime import datetime -import getmac # noqa: E402 +import getmac # -- Project information ----------------------------------------------------- @@ -35,71 +19,26 @@ extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', - 'sphinx_autodoc_typehints', - 'sphinx_inline_tabs', - 'sphinx_copybutton', - 'sphinx_argparse_cli', - 'sphinx_automodapi.automodapi', + 'sphinx.ext.intersphinx', + 'sphinx.ext.napoleon', # Google and NumPy style docstrings + 'sphinx_autodoc_typehints', # Type hints in docstrings + 'sphinx_inline_tabs', # Adds directive: '.. tab:: ' + 'sphinx_copybutton', # Adds copy button to code blocks + 'sphinx_argparse_cli', # Adds CLI documentation from argparse + 'sphinx_automodapi.automodapi', # API documentation + 'sphinx_issues', # GitHub Issues/PRs - :issue:, :pr: ] -# TODO: setup ReadTheDocs -# TODO: convert docs to RST format -source_parsers = { - '.md': 'recommonmark.parser.CommonMarkParser', +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), } -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -source_suffix = ['.md'] # , '.rst' - -# The master toctree document. -master_doc = 'index' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' # None - # -- Options for HTML output ------------------------------------------------- -# TODO: furo theme? (like the pip documentation) -html_theme = 'sphinx_rtd_theme' +html_theme = 'furo' # html_theme_options = {} -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = [] # '_static' - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -html_sidebars = { - '**': [ - 'relations.html', # needs 'show_related': True theme option to display - 'searchbox.html', - ] -} - -html_context = { - "display_github": True, # Integrate GitHub - "github_user": "GhostofGoes", # Username - "github_repo": "getmac", # Repo name - "github_version": "main", # Version - "conf_py_path": "/", # Path in the checkout to the docs root -} - # -- Options for manual page output ------------------------------------------ @@ -107,17 +46,16 @@ # (source start file, name, description, authors, manual section). # TODO: auto-build and include the man page in the release archive man_pages = [ - (master_doc, 'getmac', 'Cross-platform Python package to get MAC addresses', + ('index', 'getmac', 'Cross-platform Python package to get MAC addresses', [author], 1) ] -# -- Extension configuration ------------------------------------------------- +# -- Options for automodapi -------------------------------------------------- + +automodapi_inheritance_diagram = False + + +# -- Options for sphinx_issues ----------------------------------------------- -def setup(app): - app.add_config_value('recommonmark_config', { - 'auto_toc_tree_section': 'Contents', - 'enable_eval_rst': True, - 'enable_auto_doc_ref': True, - }, True) - app.add_transform(AutoStructify) +issues_github_path = "ghostofgoes/getmac" diff --git a/docs/examples.rst b/docs/examples.rst deleted file mode 100644 index e69de29..0000000 From 5185451510a0750400c5c69c57c99d803528dc78 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 12 Jul 2025 14:01:25 -0600 Subject: [PATCH 075/112] doc: refresh contributor guide --- CONTRIBUTING.md | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2b7c594..ecb96a3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,21 +3,21 @@ Thanks for taking an interest in this awesome little project. We love to bring new members into the community, and can always use the help. ## Resources -* Task tracking and Bug reports: [GitHub](https://github.com/GhostofGoes/getmac/issues) -* Discussion: the [Python Discord server](https://discord.gg/python) +* Features suggestions and bug reports: [GitHub](https://github.com/GhostofGoes/getmac/issues) +* Discussion and general questions/help: [GitHub discussions](https://github.com/GhostofGoes/getmac/discussions) or the [Python Discord server](https://discord.gg/python) # Code requirements Your code *must*: * Have tests -* Work with all supported Python versions -* Work on all supported platforms -* Pass linting -* Pass CI +* Work with all *supported* Python versions +* Work on all *supported* platforms +* Pass linting (code quality checks) +* Pass CI (GitHub Actions) * Adhere to the [Code of Conduct](CODE_OF_CONDUCT.md) -Most of these requirements are checked in CI (GitHub Actions), including Python versions and most supported platforms. Code is formatted with [Black](https://github.com/psf/black). You can write whatever format you want, as long as you run Black (`pdm run format`) before pushing, you're good. +Most of these requirements are checked in CI (GitHub Actions), including Python versions and most supported platforms. Code is formatted with [Black](https://github.com/psf/black). You can write whatever format you want, as long as you run Black (`pdm run format`) before pushing, you're good. Please be respectful and follow the [Code of Conduct](CODE_OF_CONDUCT.md). Memes, references, and jokes are OK. Be nice, we're all human, and code is the great equalizer. @@ -54,12 +54,12 @@ Please be respectful and follow the [Code of Conduct](CODE_OF_CONDUCT.md). Memes ## Platform testing Help is dearly needed on testing and rooting out differences in various platforms and configurations. At a basic level, this involves just running the tests on any platforms you use. Open issues for any bugs or quirks you discover, or if you're feeling adventurous, fix it yourself! -Any platform is fair game! The following are some highlights or platforms of special interest: +**Any platform is fair game!** The following are platforms of special interest: * MacOS/OSX (This requires owning a Mac, and is the area most sorely in need of testing) -* Legacy Windows desktop (7 and 8) -* Windows Server +* Legacy Windows (7, 8, 8.1) +* Windows Server (all versions) * Arch Linux -* BSD +* BSDs ## Sample collection Examples of output of various commands is an easy way contribute that is still incredibly helpful. @@ -77,17 +77,18 @@ Examples of output of various commands is an easy way contribute that is still i ```bash pdm install -d ``` -4. Ensure tests work locally: +4. Ensure tests and linting works: ```bash - # Run code quality checks pdm run lint - - # Run the tests pdm run test ``` 5. Write some code! Git commit messages should information about what changed, and if it's relevant, the rationale (thinking) for the change. -6. Follow the checklist in the pull request template -7. Submit a pull request! +6. Format your code: + ```bash + pdm run format + ``` +7. Follow the checklist in the pull request template +8. Submit a pull request! # Bug reports @@ -137,3 +138,12 @@ pdm run lint # Run getmac CLI pdm run getmac ``` + +# Documentation + +The docs are built using Sphinx. They are located in the `docs/` folder, and the configuration is in `docs/conf.py`. + +To build docs locally: +```shell +pdm run docs +``` From 675f938d94e6a1b5f673e8a05c4511af4c9a7a27 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 12 Jul 2025 14:01:35 -0600 Subject: [PATCH 076/112] doc: refresh bug report template --- .github/ISSUE_TEMPLATE/bug_report.md | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 3af1165..5981d9d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,21 +4,25 @@ about: Report errors, bugs, or anything else that isn't working as expected. --- -**Describe the bug** -A clear and concise description of what the bug is. +## Describe the bug +A clear and concise description in English of the bug/issue. Include the command(s) run or code used, and any relevant output. Please include **text output** in English. For output that exceeds a ~1000 characters, use a [GitHub Gist](https://gist.github.com/) and include a link in the issue. Screenshots or photos of screens are not acceptable. -**To Reproduce** +## To Reproduce Steps to reproduce the behavior [if applicable]. -**Expected behavior** +## Expected behavior A clear and concise description of what you expected to happen. -**System info** -(please complete the following information): - - OS name (e.g. Windows 10 x64): - - OS Version (e.g. 1804 build 17134): - - Python version (e.g. CPython 3.6.5 x64): - - getmac version (e.g. getmac 0.8.3): +## System information +**Please complete the following sections**: +- OS name, version, and architecture: +- Python interpreter and version: +- `getmac` version: -**Additional context** -Add any other context about the problem here. +Example: +- OS name, version, and architecture: Windows 10 1804 build 17134 x64 +- Python interpreter and version: CPython 3.6.5 x64 +- `getmac` version (run `python3 -m getmac --version`): getmac 0.8.3 + +## Additional context +Add any other context about the problem here, and links to any related issues [if applicable]. From fb081712a80b2a81be26ddfea9dd297b2f1b26b6 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 12 Jul 2025 14:02:18 -0600 Subject: [PATCH 077/112] refactor: move arg parsing into function --- getmac/__main__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/getmac/__main__.py b/getmac/__main__.py index 8e97d5b..91f86dd 100644 --- a/getmac/__main__.py +++ b/getmac/__main__.py @@ -8,7 +8,7 @@ from .variables import settings, gvars -def main() -> None: +def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( prog="getmac", description="Get MAC addresses of network interfaces or LAN hosts", @@ -84,6 +84,11 @@ def main() -> None: "compatibility, and Method.test() will NOT be checked!", ) + return parser + + +def main() -> None: + parser = build_parser() args = parser.parse_args() if args.debug or args.verbose: From 885675edf3178e9b1d59c25d946efc7998b43a71 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 12 Jul 2025 14:03:40 -0600 Subject: [PATCH 078/112] doc: initial stab at docs refresh - Install guide - CLI guide - API documentation - Usage examples --- docs/api.rst | 11 +++++ docs/cli.rst | 98 +++++++++++++++++++++++++++++++++---------- docs/index.rst | 45 ++++++-------------- docs/installation.rst | 58 +++++++++++++++++++++++++ docs/usage.rst | 43 +++++++++++++++++++ 5 files changed, 199 insertions(+), 56 deletions(-) create mode 100644 docs/installation.rst create mode 100644 docs/usage.rst diff --git a/docs/api.rst b/docs/api.rst index 139597f..fd6b18e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,2 +1,13 @@ +=== +API +=== +Full API reference for the `getmac` package. +.. automodapi:: getmac.getmac + :skip: CalledProcessError,IPv4Address,IPv4Interface,IPv4Network,IPv6Address,IPv6Interface,IPv6Network + +.. automodapi:: getmac.variables + :skip: VarsClass + +.. automodapi:: getmac.utils diff --git a/docs/cli.rst b/docs/cli.rst index c2d9059..763ee4d 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -1,14 +1,17 @@ +====================== +Command-Line Interface +====================== + +The ``getmac`` command provides developers and power users a cross-platform command line tool to get MAC addresses. It is installed when the pip package is installed. Basic Usage =========== -The ``getmac`` command is included when you install the package. This provides developers and power users (like you!) a cross-platform tool to get MAC addresses. - .. note:: - Depending on your Python environment, you may be able to invoke it directly (``getmac``) or have to reference your Python interpreter to invoke it. See :ref:`python-invoke` for details. + Depending on your Python environment, you may not be able to run the command directly (e.g. ``getmac``). If this is the case, then it will have to be invoked via the Python interpreter. See :ref:`python-invoke` for details. .. note:: - On Windows, ``getmac.exe`` is a system binary. Depending on the value of the ``PATH`` environment variable, it may get prioritized over the ``getmac`` shim installed by pip. Ensure the ``\Scripts\`` folder in site-packages is added to the PATH variable for your user! + On Windows, ``getmac.exe`` is a *system binary* included with Windows. Depending on the value of the ``PATH`` environment variable, it may get prioritized over the ``getmac`` executable shim installed by pip. Ensure the ``\Scripts\`` folder for your Python installation is added to the ``PATH`` variable for your user! Examples: ``C:\Users\\AppData\Local\Programs\Python\Python39\Scripts``, ``C:\Users\\scoop\apps\python\current\Scripts``. .. code-block:: shell @@ -19,7 +22,7 @@ The ``getmac`` command is included when you install the package. This provides d # Print the current version getmac --version - # Invoking with no arguments will return MAC address of the default network interface + # No arguments returns the MAC address of the default network interface getmac # Interface names, IPv4/IPv6 addresses, or Hostnames can be specified @@ -43,11 +46,36 @@ Getting the MAC address of a remote host requires the ARP table to be populated. getmac --no-network-request -n home.router + +Advanced usage +============== + +Controlling method selection +---------------------------- +getmac has a selection of methods used to acquire information across platforms. The methods it uses are determined by the platform and if required commands/libraries are present. This system isn't perfect, and in some cases it can be beneficial to have control over method selection, such as in the case of debugging. + +The platform detected by getmac can be overridden via ``--override-platform``. This is useful when debugging issues or if you know a method for a different platform works on the current platform. Any values returned by ``platform.system()`` are valid. + +.. code-block:: shell + + # Force "linux" to be the "detected" platform and use "linux" methods + getmac -i eth0 --override-platform linux + + # Force "windows" to be the "detected" platform and use "windows" methods + getmac --ip 192.168.0.1 --override-platform windows + + +A specific method can also be used with ``--force-method``, regardless of the consequences or if it even works. + +.. code-block:: shell + + getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost + + Enabling logging messages and debugging --------------------------------------- .. note:: - When reporting an issue or asking for help, please enable verbose and the highest level of debugging: ``getmac -v -dddd [arguments]`` Normally, no log messages are printed, just the result from the command. Adding ``-v`` (``--verbose``) argument to any command will enable these messages. This is useful for debugging issues or understanding what's happening. @@ -80,36 +108,60 @@ There is also a debugging mode. There are multiple levels of debugging, up to 4. +.. _python-invoke: -.. ref: python-invoke - -Usage by specifying Python interpreter --------------------------------------- +Invoking via interpreter +======================== If ``getmac`` doesn't work, try ``python3 -m getmac``, where ``python3`` is whatever command you use to run Python and used to install the package. On Windows, this may be ``py``, e.g. ``py -m getmac``, or just ``python``, e.g. ``python -m getmac``. -When invoking in this fashion, in the examples above you can simply replace calls to ``getmac`` with ``python3 -m getmac`` (or whatever invocation applies to your platform). +When invoking in this fashion, replace calls to ``getmac`` in the examples above with ``python3 -m getmac`` (or whatever invocation applies to your platform). -.. code-block:: shell +.. tab:: Linux/macOS + + .. code-block:: shell + + python3 -m getmac + python3 -m getmac --help + python3 -m getmac --version + +.. tab:: Windows - python3 -m getmac - python3 -m getmac --help - python3 -m getmac --version + .. code-block:: shell + + py -m getmac + py -m getmac --help + py -m getmac --version Examples of running as a Python module with shorthands for the arguments -.. code-block:: shell +.. tab:: Linux/macOS + + .. code-block:: shell + + python3 -m getmac -i 'Ethernet 4' + python3 -m getmac -4 192.168.0.1 + python3 -m getmac -6 ::1 + python3 -m getmac -n home.router + +.. tab:: Windows + + .. code-block:: shell + + py -m getmac -i 'Ethernet 4' + py -m getmac -4 192.168.0.1 + py -m getmac -6 ::1 + py -m getmac -n home.router - python3 -m getmac -i 'Ethernet 4' - python3 -m getmac -4 192.168.0.1 - python3 -m getmac -6 ::1 - python3 -m getmac -n home.router +.. _args: -Arguments -========= +Arguments reference +=================== - Put requisite calls to sphinx_argparse_cli here.=== +.. sphinx_argparse_cli:: + :module: getmac.__main__ + :func: build_parser diff --git a/docs/index.rst b/docs/index.rst index a37f2a8..1668724 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,38 +1,17 @@ +getmac is a pure-Python package to get the MAC addresses of network interfaces and hosts on the local network. +It provides a platform-independent interface to get the MAC addresses of: +- System network interfaces (by interface name) +- Remote hosts on the local network (by IPv4/IPv6 address or hostname) +It provides a Python function, :func:`~getmac.getmac.get_mac_address`, and a command-line interface, :ref:`getmac `. -Installation -============ +.. toctree:: + :hidden: + :titlesonly: -Stable release from PyPI ------------------------- - -.. code-block:: shell - - pip install getmac - - -Latest development version --------------------------- -This will install the current version from the main branch on GitHub. - -.. code-block:: shell - - pip install https://github.com/ghostofgoes/getmac/archive/main.tar.gz - - -Debian-based distributions --------------------------- - -``apt`` may have it available, depending on your distro (see packaging status below): - -.. code-block:: shell - - sudo apt install getmac - - -OS Packaging Status -------------------- - -[![Packaging status](https://repology.org/badge/vertical-allrepos/getmac.svg)](https://repology.org/project/getmac/versions) + installation + usage + cli + api diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 0000000..469ac8d --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,58 @@ +============ +Installation +============ + +Stable release from PyPI +======================== + +.. tab:: Linux/macOS + + .. code-block:: shell + + python3 -m pip install getmac + +.. tab:: Windows + + .. code-block:: powershell + + py -m pip install getmac + + +Debian-based Linux distributions +================================ + +``apt`` may have it available, depending on the Linux distribution (see packaging status below). + +.. code-block:: shell + + sudo apt install getmac + + +OS Packaging Status +=================== + +.. image:: https://repology.org/badge/vertical-allrepos/getmac.svg + :alt: OS Packaging status + :target: https://repology.org/project/getmac/versions + + +Latest development version +========================== + +.. warning:: + Installing directly from main includes pre-release changes and is not recommended for most users. + +This will install the latest version from the main branch on GitHub. + +.. tab:: Linux/macOS + + .. code-block:: shell + + python3 -m pip install https://github.com/ghostofgoes/getmac/archive/main.tar.gz + +.. tab:: Windows + + .. code-block:: powershell + + py -m pip install https://github.com/ghostofgoes/getmac/archive/main.tar.gz + diff --git a/docs/usage.rst b/docs/usage.rst new file mode 100644 index 0000000..bd36dc2 --- /dev/null +++ b/docs/usage.rst @@ -0,0 +1,43 @@ +===== +Usage +===== + +getmac provides a Python function, :func:`~getmac.getmac.get_mac_address`, and a command-line interface, :ref:`getmac `. The command-line interface is documented in :doc:`cli`. This page documents usage from Python as a library. + +Examples +======== + +.. code-block:: python + + from getmac import get_mac_address + + eth_mac = get_mac_address(interface="eth0") + win_mac = get_mac_address(interface="Ethernet 3") + ip_mac = get_mac_address(ip="192.168.0.1") + ip6_mac = get_mac_address(ip6="::1") + host_mac = get_mac_address(hostname="localhost") + updated_mac = get_mac_address(ip="10.0.0.1", network_request=True) + + # Enable debugging + from getmac import getmac + getmac.DEBUG = 2 # DEBUG level 2 + print(getmac.get_mac_address(interface="Ethernet 3")) + + # Change the UDP port used for updating the ARP table (UDP packet) + from getmac import getmac + getmac.PORT = 55555 # Default port is 55555 + print(getmac.get_mac_address(ip="192.168.0.1", network_request=True)) + + +Configuration +============= + +TODO: update these for new settings classes + + + +- ``logging.getLogger("getmac")``: Runtime messages and errors are recorded to the ``getmac`` logger using Python's :mod:`logging` module. They can be configured by using :func:`logging.basicConfig` or adding :class:`logging.Handler` instances to the logger named ``"getmac"``. +- :attr:`~getmac.variables.Settings.DEBUG`: integer value that controls debugging output. The higher the value, the more output you get. +- :attr:`~getmac.variables.Settings.PORT`: the UDP port used to populate the ARP table (IPv4) or NDP list (IPv6) when looking up MACs for hosts or IPs (see the documentation of the ``network_request`` argument in :func:`~getmac.getmac.get_mac_address` for details). +- :attr:`getmac.variables.Settings.OVERRIDE_PLATFORM`: Override the platform detection with the given value (e.g. ``"linux"``, ``"windows"``, ``"freebsd"``, etc). Any values returned by :func:`platform.system` are valid. +- :attr:`~getmac.variables.Settings.FORCE_METHOD`: Name of method to use. This will force a specific method to be used, e.g. :class:`getmac.getmac.IpNeighborShow` with the string ``"IpNeighborShow"``. This will be used regardless of the method's type or platform compatibility, and :func:`Method.test() ` will NOT be checked! The list of available methods is in :data:`getmac.getmac.METHODS`. From 06625964ece029433cbc55ddd1a525485d77643b Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 12 Jul 2025 14:08:26 -0600 Subject: [PATCH 079/112] doc: overhaul source code docs - add docstrings to classes that were missing them - Use docstrings instead of sphinx-style inline comments for class variables - Improve and fix existing docstrings - Add Sphinx directives to link to functions/docs - Mark more variables as Final --- getmac/getmac.py | 206 ++++++++++++++++++++++++++++++++---------- getmac/utils.py | 33 +++---- getmac/variables.py | 215 ++++++++++++++++++++++++++++++-------------- 3 files changed, 328 insertions(+), 126 deletions(-) diff --git a/getmac/getmac.py b/getmac/getmac.py index 54b04e6..8ba5478 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -6,7 +6,7 @@ - System network interfaces (by interface name) - Remote hosts on the local network (by IPv4/IPv6 address or hostname) -It provides one function: ``get_mac_address()`` +The key function is :func:`~getmac.getmac.get_mac_address`. .. code-block:: python :caption: Examples @@ -47,8 +47,12 @@ class Method: - #: Valid platform identifier strings - VALID_PLATFORM_NAMES: Set[str] = { + """ + Base class defining a method to get a MAC address. + Subclasses should implement the :meth:`test` and :meth:`get` methods. + """ + + VALID_PLATFORM_NAMES: Final[Set[str]] = { "android", "darwin", "linux", @@ -59,24 +63,43 @@ class Method: "sunos", "other", } + """ + The valid platform identifier strings, used to match + methods to the appropriate platform. + """ - #: Platforms supported by a method platforms: Set[str] = set() + """ + Platforms supported by a method. + """ - #: The type of method, e.g. does it get the MAC of a interface? - #: Allowed values: {ip, ip4, ip6, iface, default_iface} method_type: str = "" + """ + The type of method, e.g. does it get the MAC of a interface. + + Allowed values: + + - ip + - ip4 + - ip6 + - iface + - default_iface + """ - #: If the method makes a network request as part of the check network_request: bool = False + """ + If the method makes a network request as part of the check. + """ - #: Marks the method as unable to be used, e.g. if there was a runtime - #: error indicating the method won't work on the current platform. unusable: bool = False + """ + Marks the method as unable to be used, e.g. if there was a runtime + error indicating the method won't work on the current platform. + """ def test(self) -> bool: """ - Low-impact test that the method is feasible, e.g. command exists. + Low-impact test that the method is feasible, e.g. a command exists. """ return False # pragma: no cover @@ -92,11 +115,11 @@ def get(self, arg: str) -> Optional[str]: # noqa: ARG002 Args: arg: What the method should get, such as an IP address - or interface name. In the case of default_iface methods, + or interface name. In the case of ``default_iface`` methods, this is not used and defaults to an empty string. Returns: - Lowercase colon-separated MAC address, or None if one could + Lowercase colon-separated MAC address, or :obj:`None` if one could not be found. """ return None # pragma: no cover @@ -109,6 +132,11 @@ def __str__(cls) -> str: # TODO(python3): do we want to keep this around? It calls 3 commands and is # quite inefficient. We should just take the methods and use directly. class UuidArpGetNode(Method): + """ + Uses Python's :func:`uuid._arp_getnode` function to get the + MAC address of a remote host using ARP. + """ + platforms = {"linux", "darwin", "sunos", "other"} method_type = "ip" @@ -142,6 +170,10 @@ def get(self, arg: str) -> Optional[str]: class ArpFile(Method): + """ + Use the contents of ``/proc/net/arp`` to find the MAC address of a host. + """ + platforms = {"linux"} method_type = "ip4" @@ -175,6 +207,10 @@ def get(self, arg: str) -> Optional[str]: class ArpFreebsd(Method): + """ + Use the ``arp`` command to find the MAC address of a host on FreeBSD. + """ + platforms = {"freebsd"} method_type = "ip" @@ -187,6 +223,10 @@ def get(self, arg: str) -> Optional[str]: class ArpOpenbsd(Method): + """ + Use the ``arp`` command to find the MAC address of a host on OpenBSD. + """ + platforms = {"openbsd"} method_type = "ip" @@ -200,10 +240,15 @@ def get(self, arg: str) -> Optional[str]: class ArpVariousArgs(Method): + """ + Use the ``arp`` command to find the MAC address of a host on various platforms. + """ + platforms = {"linux", "darwin", "freebsd", "sunos", "other"} method_type = "ip" - _regex_std: str = r"\)\s+at\s+" + consts.MAC_RE_COLON - _regex_darwin: str = r"\)\s+at\s+" + consts.MAC_RE_SHORT + + _regex_std: Final[str] = r"\)\s+at\s+" + consts.MAC_RE_COLON + _regex_darwin: Final[str] = r"\)\s+at\s+" + consts.MAC_RE_SHORT _args = ( ("", True), # "arp 192.168.1.1" # Linux @@ -292,29 +337,31 @@ def get(self, arg: str) -> Optional[str]: class ArpingHost(Method): """ - Use ``arping`` command to determine the MAC of a host. + Use ``arping`` command to determine the MAC of a host + on Linux and Darwin (MacOS). - Supports three variants of ``arping`` + This method supports three variants of ``arping``: - "habets" arping by Thomas Habets (`GitHub `__) On Debian-based distros, ``apt install arping`` will install Habets arping. - - "iputils" arping, from the ``iputils-arping`` - `package `__ + - "iputils" arping, from the + `iputils-arping package `__ - "busybox" arping, included with BusyBox (a small executable "distro") (`further reading `__) BusyBox's arping quite similar to iputils-arping. The arguments for our purposes are the same, and the output is also the same. - There's even a TODO in busybox's arping code referencing iputils arping. + There's even a TODO in BusyBox's arping code referencing iputils arping. There are several differences: + - The return code from bad arguments is 1, not 2 like for iputils-arping - The MAC address in output is lowercase (vs. uppercase in iputils-arping) This was a pain to obtain samples for busybox on Windows. I recommend using WSL and arping'ing the Docker gateway (for WSL2 distros). - NOTE: it must be run as root using ``sudo busybox arping``. + Note, it must be run as root using ``sudo busybox arping``. """ platforms = {"linux", "darwin"} @@ -375,6 +422,9 @@ class CtypesHost(Method): Uses ``SendARP`` from the Windows ``Iphlpapi`` to get the MAC address of a remote IPv4 host. + .. note:: + This doesn't work with IPv6. + Microsoft Documentation: `SendARP function (iphlpapi.h) `__ """ @@ -422,6 +472,11 @@ def get(self, arg: str) -> Optional[str]: class IpNeighborShow(Method): + """ + Uses the ``ip neighbor show`` command to get the MAC address + of a remote host. + """ + platforms = {"linux", "other"} method_type = "ip" # IPv6 and IPv4 @@ -444,6 +499,11 @@ def get(self, arg: str) -> Optional[str]: class SysIfaceFile(Method): + """ + Uses the contents of ``/sys/class/net//address`` + to get the MAC address of a interface. + """ + platforms = {"linux", "wsl"} method_type = "iface" @@ -463,6 +523,11 @@ def get(self, arg: str) -> Optional[str]: class UuidLanscan(Method): + """ + Uses Python's :func:`uuid._find_mac` function to get the MAC address + using the ``lanscan`` command on platforms that support it (HP-UX). + """ + platforms = {"other"} method_type = "iface" @@ -486,6 +551,11 @@ def get(self, arg: str) -> Optional[str]: class FcntlIface(Method): + """ + Uses :func:`fcntl.ioctl` to get the MAC address of a network + interface on Linux (including WSL). + """ + platforms = {"linux", "wsl"} method_type = "iface" @@ -668,7 +738,7 @@ def _parse_ifconfig(iface: str, command_output: str) -> Optional[str]: class IfconfigWithIfaceArg(Method): """ ``ifconfig`` command with the interface name as an argument - (e.g. ``ifconfig eth0``). + (e.g. ``ifconfig eth0``) to determine MAC address of an interface. """ platforms = {"linux", "wsl", "freebsd", "openbsd", "other"} @@ -693,6 +763,11 @@ def get(self, arg: str) -> Optional[str]: # TODO: combine this with IfconfigWithArg/IfconfigNoArg # (need to do live testing on Darwin) class IfconfigEther(Method): + """ + Determine interface MAC using ``ifconfig`` command + on Darwin (MacOS) systems. + """ + platforms = {"darwin"} method_type = "iface" @@ -726,7 +801,8 @@ def get(self, arg: str) -> Optional[str]: # TODO: unit tests class IfconfigOther(Method): """ - Wild 'Shot in the Dark' attempt at ``ifconfig`` for unknown platforms. + Wild 'Shot in the Dark' attempt at using ``ifconfig`` + to get interface MAC on unknown platforms. """ platforms = {"linux", "other"} @@ -793,6 +869,10 @@ def get(self, arg: str) -> Optional[str]: class NetstatIface(Method): + """ + Determines interface MAC using the ``netstat`` command. + """ + platforms = {"linux", "wsl", "other"} method_type = "iface" @@ -851,6 +931,10 @@ def get(self, arg: str) -> Optional[str]: class IpLinkIface(Method): + """ + Determines interface MAC using the ``ip link`` command. + """ + platforms = {"linux", "wsl", "android", "other"} method_type = "iface" @@ -889,7 +973,8 @@ def get(self, arg: str) -> Optional[str]: class DefaultIfaceLinuxRouteFile(Method): """ - Get the default interface by parsing the ``/proc/net/route`` file. + Determine the default interface by parsing the ``/proc/net/route`` file + on Linux-based platforms (including WSL). This is the same source as the ``route`` command, however it's much faster to read this file than to call ``route``. If it fails for whatever @@ -935,6 +1020,10 @@ def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 class DefaultIfaceRouteCommand(Method): + """ + Determine default interface using the ``route -n`` command. + """ + platforms = {"linux", "wsl", "other"} method_type = "default_iface" @@ -956,6 +1045,11 @@ def get(self, arg: str = "") -> Optional[str]: class DefaultIfaceRouteGetCommand(Method): + """ + Determine default interface using the ``route get default`` command + on BSD-based platforms, including Darwin (MacOS). + """ + platforms = {"darwin", "freebsd", "other"} method_type = "default_iface" @@ -976,6 +1070,10 @@ def get(self, arg: str = "") -> Optional[str]: class DefaultIfaceIpRoute(Method): + """ + Determine the default interface using the ``ip route`` command. + """ + # NOTE: this is slightly faster than "route" since # there is less output than "route -n" platforms = {"linux", "wsl", "other"} @@ -996,6 +1094,12 @@ def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 class DefaultIfaceOpenBsd(Method): + """ + Determine the default interface on OpenBSD using the ``route`` command. + + The full command is ``route -nq show -inet -gateway -priority 1``. + """ + platforms = {"openbsd"} method_type = "default_iface" @@ -1008,6 +1112,12 @@ def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 class DefaultIfaceFreeBsd(Method): + """ + Determine the default interface on FreeBSD using the ``netstat`` command. + + The full command is ``netstat -r``. + """ + platforms = {"freebsd"} method_type = "default_iface" @@ -1052,26 +1162,33 @@ def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 DefaultIfaceFreeBsd, ] # type: List[Type[Method]] -# TODO: move to gvars class? gotta love import loops with type annotations -# Primary method to use for a given method type +# TODO: move to gvars class? gotta love import loops with type annotations. +# Use deferred annotations/string annotations. METHOD_CACHE: Dict[str, Optional[Method]] = { "ip4": None, "ip6": None, "iface": None, "default_iface": None, } +""" +Primary method to use for a given method type +""" -# Order of methods is determined by: -# Platform + version -# Performance (file read > command) -# Reliability (how well I know/understand the command to work) +# TODO: move to gvars class? FALLBACK_CACHE: Dict[str, List[Method]] = { "ip4": [], "ip6": [], "iface": [], "default_iface": [], } +""" +Order of methods is determined by: + +- Platform + version +- Performance (file read > command) +- Reliability (how well I know/understand the command to work) +""" def get_method_by_name(method_name: str) -> Optional[Type[Method]]: @@ -1423,28 +1540,26 @@ def get_mac_address( network_request: bool = True, ) -> Optional[str]: """ - Get a MAC from a local interface or remote host. + Get a MAC address from a local interface or remote host. - If you want to be pedantic, this is (probably) - a unicast IEEE 802 MAC-48 address. - - Only ONE of the first four arguments may be used - (``interface``,``ip``, ``ip6``, or ``hostname``). + Only ONE of the first four arguments may be used: + ``interface``, ``ip``, ``ip6``, or ``hostname``. If none of the arguments are selected, the default network interface for the system will be used. + The MAC is usually a unicast IEEE 802 MAC-48 address. + .. warning:: - In getmac 1.0.0, exceptions will be raised if the method cache initialization fails - (in other words, if there are no valid methods found for the type of MAC requested). + In getmac 1.0.0, exceptions will be raised if there are no valid methods + found for the type of MAC requested. .. note:: ``"localhost"`` or ``"127.0.0.1"`` will always return ``"00:00:00:00:00:00"`` .. note:: - It is assumed that you are using Ethernet or Wi-Fi. While other protocols + It is assumed that the host is using Ethernet or Wi-Fi. While other protocols such as Bluetooth may work, this has not been tested and should not be - relied upon. If you need this functionality, please open an issue - (or better yet, a Pull Request ;))! + relied upon. If this functionality is needed, please open an issue or PR. .. note:: Exceptions raised by methods are handled silently and returned as :obj:`None`. @@ -1458,17 +1573,18 @@ def get_mac_address( and treat them as if ``ip6`` argument was set instead. ip6: Canonical shortened IPv6 address of a remote host (e.g ``ff02::1:ffe7:7f19``), or a :mod:`ipaddress` object (:class:`~ipaddress.IPv6Address` - and :class:`~ipaddress.IPv6Interface`). + or :class:`~ipaddress.IPv6Interface`). hostname: DNS hostname of a remote host (e.g "router1.mycorp.com", "localhost") network_request: If network requests should be made when attempting to find the MAC of a remote host. If the ``arping`` command is available, this will be used. If not, a UDP packet will be sent to the remote host to populate the ARP/NDP tables for IPv4/IPv6. The port this packet is sent to can - be configured using the setting ``getmac.settings.PORT``. + be configured using the setting :attr:`getmac.variables.Settings.PORT` + (by default, it's port 55555). Returns: - Lowercase colon-separated MAC address, or :obj:`None` if one could not be - found or there was an error. + Lowercase colon-separated MAC address. If no MAC was found, or an exception + occurred, :obj:`None` is returned. """ # If debugging, start the timer @@ -1477,7 +1593,7 @@ def get_mac_address( start_time = timeit.default_timer() - # Convert bytes to str + # Convert bytes to str, assuming UTF-8 encoding if isinstance(interface, bytes): interface = interface.decode("utf-8") if isinstance(ip, bytes): diff --git a/getmac/utils.py b/getmac/utils.py index b08a2c1..369eea8 100644 --- a/getmac/utils.py +++ b/getmac/utils.py @@ -8,8 +8,8 @@ import re import shlex import socket -from shutil import which -from subprocess import DEVNULL, check_output +import subprocess +import shutil from typing import Optional, Union from .variables import settings, consts, gvars @@ -18,7 +18,7 @@ def check_command(command: str) -> bool: """ Check if a command exists using :func:`shutil.which`. The result of the check - is cached in a global dict to speed up subsequent lookups. + is cached in a global :class:`dict` to speed up subsequent lookups. Args: command: command to check @@ -27,13 +27,13 @@ def check_command(command: str) -> bool: If the command exists """ if command not in gvars.CHECK_COMMAND_CACHE: - gvars.CHECK_COMMAND_CACHE[command] = bool(which(command, path=gvars.PATH_STR)) + gvars.CHECK_COMMAND_CACHE[command] = bool(shutil.which(command, path=gvars.PATH_STR)) return gvars.CHECK_COMMAND_CACHE[command] def check_path(filepath: str) -> bool: """ - Check if the file pointed to by `filepath` exists and is readable. + Check if the file pointed to by ``filepath`` exists and is readable. Args: filepath: absolute path of file to check @@ -134,9 +134,9 @@ def search( Args: regex: regular expression text: data to search - group_index: what index in the ``groupdict`` to return, - if there are more than 1 - flags: :mod:`re` flags + group_index: index of value in the ``groupdict`` to return, + if there are multiple groups in the regex + flags: flags to :mod:`re` functions, e.g. :const:`re.IGNORECASE` Returns: The result, or :obj:`None` if the parsing failed @@ -158,10 +158,10 @@ def popen(command: str, args: str) -> str: """ Execute a command with arguments and return the stdout (stderr is discarded). - Wrapper around :func:`~getmac.utils.call_proc`, with checks + Wrapper around :func:`getmac.utils.call_proc`, with checks to ensure the command exists and is executable and some debug logging. This should be used instead of - :func:`~getmac.utils.call_proc`. + :func:`getmac.utils.call_proc`. Args: command: command to run, e.g. ``ping`` or ``ping.exe`` @@ -196,9 +196,10 @@ def popen(command: str, args: str) -> str: def call_proc(executable: str, args: str) -> str: """ Wrapper around :func:`subprocess.check_output` with some - logging and type conversion. The reason this and - :func:`~getmac.utils.popen` are separate functions is - for testability. + logging and type conversion. + + The reason this and :func:`getmac.utils.popen` are separate + functions is to make it easier to mock for unit tests. Args: executable: command to run @@ -215,7 +216,7 @@ def call_proc(executable: str, args: str) -> str: else: cmd = [executable, *shlex.split(args)] # type: ignore - output: Union[str, bytes] = check_output(cmd, stderr=DEVNULL, env=gvars.ENV) + output: Union[str, bytes] = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, env=gvars.ENV) if settings.DEBUG >= 4: gvars.log.debug(f"Output from '{executable}' command: {output!s}") @@ -228,10 +229,10 @@ def call_proc(executable: str, args: str) -> str: def uuid_convert(mac: int) -> str: """ - Convert value output from ``uuid`` internal function into a string. + Convert value output from :mod:`uuid` internal function into a string. Args: - mac: integer value returned from a ``uuid`` function + mac: integer value returned from a :mod:`uuid` function Returns: String with colon-separated MAC address diff --git a/getmac/variables.py b/getmac/variables.py index 6a94a20..74f4918 100644 --- a/getmac/variables.py +++ b/getmac/variables.py @@ -1,3 +1,7 @@ +""" +Global variables, constants, and settings for the getmac package. +""" + import logging import os import platform @@ -14,19 +18,28 @@ class Settings(VarsClass): User-configurable settings. """ - #: Debugging level. Increased value => more output. 4 is roughly the highest. DEBUG: int = 0 + """ + Debugging level. Increased value => more output. 4 is roughly the highest. + """ - #: UDP port to use for populating the ARP table PORT: int = 55555 + """ + UDP port to use for populating the ARP table (IPv4) or NDP list (IPv6) + when looking up MACs for hosts or IPs. + """ - #: User-configurable override to force a specific platform # TODO: This will change to a function argument in 1.0.0 OVERRIDE_PLATFORM: str = "" + """ + User-configurable override to force a specific platform. + """ - #: Force a specific method to be used for all lookups - #: Used for debugging and testing FORCE_METHOD: str = "" + """ + Force a specific method to be used for all lookups. + Used for debugging and testing. + """ class Constants(VarsClass): @@ -34,90 +47,162 @@ class Constants(VarsClass): Platform identifiers and other constants. """ - _UNAME: platform.uname_result = platform.uname() - _SYST: str = _UNAME.system - WINDOWS: bool = _SYST == "Windows" - DARWIN: bool = _SYST == "Darwin" - OPENBSD: bool = _SYST == "OpenBSD" - FREEBSD: bool = _SYST == "FreeBSD" - NETBSD: bool = _SYST == "NetBSD" - SOLARIS: bool = _SYST == "SunOS" - #: Not including Darwin or Solaris as a "BSD" - BSD: bool = OPENBSD or FREEBSD or NETBSD - - #: Windows Subsystem for Linux (WSL) - #: WSL1: abstraction layer remapping Linux syscalls to Windows syscalls - #: WSL2: fancy Linux VM on Hyper-V - WSL1: bool = ( + _UNAME: Final[platform.uname_result] = platform.uname() + _SYST: Final[str] = _UNAME.system + + WINDOWS: Final[bool] = _SYST == "Windows" + DARWIN: Final[bool] = _SYST == "Darwin" + OPENBSD: Final[bool] = _SYST == "OpenBSD" + FREEBSD: Final[bool] = _SYST == "FreeBSD" + NETBSD: Final[bool] = _SYST == "NetBSD" + SOLARIS: Final[bool] = _SYST == "SunOS" + + BSD: Final[bool] = OPENBSD or FREEBSD or NETBSD + """ + .. note:: + This doesn't include Darwin or Solaris as a "BSD". + + :meta hide-value: + """ + + WSL1: Final[bool] = ( _SYST == "Linux" and "Microsoft" in _UNAME.version and "-WSL2" not in _UNAME.release ) - WSL2: bool = ( + """ + Windows Subsystem for Linux (WSL) version 1. + The version that's a very cool abstraction layer + remapping Linux syscalls to Windows syscalls. + + :meta hide-value: + """ + + WSL2: Final[bool] = ( _SYST == "Linux" and "Microsoft" not in _UNAME.version and "-WSL2" in _UNAME.release ) - LINUX: bool = _SYST == "Linux" and not WSL1 + """ + Windows Subsystem for Linux (WSL) version 2. + The version that's basically a fancy Linux VM on Hyper-V. + + :meta hide-value: + """ + + LINUX: Final[bool] = _SYST == "Linux" and not WSL1 + """ + If the system is running Linux (excluding WSL1). - #: NOTE: "Linux" methods apply to Android without modifications - #: If there's Android-specific stuff then we can add a platform - #: identifier for it. - ANDROID: bool = ( + :meta hide-value: + """ + + ANDROID: Final[bool] = ( hasattr(sys, "getandroidapilevel") or "ANDROID_STORAGE" in os.environ ) + """ + .. note:: + "Linux" methods apply to Android without modifications. + If there's Android-specific stuff then we can add a platform + identifier for it. + + :meta hide-value: + """ + + PLATFORM: Final[str] = "wsl" if (LINUX and WSL1) else _SYST.lower() + """ + Generic platform identifier used for filtering methods. + + # TODO: change "wsl" to "wsl1", since WSL2 method should just work like normal linux - #: Generic platform identifier used for filtering methods. - #: - #: Possible values: - #: - #: - wsl - #: - linux - #: - windows - #: - darwin - #: - openbsd - #: - freebsd - #: - netbsd - #: - sunos - #: - any other values that can be returned by :func:`platform.uname`, - #: converted to lowercase. - # TODO: change to "wsl1", since WSL2 method should just work like normal linux - PLATFORM: str = "wsl" if (LINUX and WSL1) else _SYST.lower() - - #: Regular expression pattern for MAC addresses with ':' characters - MAC_RE_COLON: str = r"([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})" - - #: Regular expression pattern for MAC addresses with '-' characters - MAC_RE_DASH: str = r"([0-9a-fA-F]{2}(?:-[0-9a-fA-F]{2}){5})" - - #: On OSX, some MACs in arp output may have a single digit instead of two - #: Examples: "18:4f:32:5a:64:5", "14:cc:20:1a:99:0" - #: This can also happen on other platforms, like Solaris - MAC_RE_SHORT: str = r"([0-9a-fA-F]{1,2}(?::[0-9a-fA-F]{1,2}){5})" + Possible values: + + - wsl + - linux + - windows + - darwin + - openbsd + - freebsd + - netbsd + - sunos + - Any other values that can be returned by :func:`platform.uname`, + converted to lowercase. + + :meta hide-value: + """ + + MAC_RE_COLON: Final[str] = r"([0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5})" + """ + Regular expression pattern for MAC addresses with ``:`` (colon) characters. + """ + + MAC_RE_DASH: Final[str] = r"([0-9a-fA-F]{2}(?:-[0-9a-fA-F]{2}){5})" + """ + Regular expression pattern for MAC addresses with ``-`` (dash) characters. + """ + + MAC_RE_SHORT: Final[str] = r"([0-9a-fA-F]{1,2}(?::[0-9a-fA-F]{1,2}){5})" + """ + On OSX, some MACs in ``arp`` output may have a single digit instead of two. + This can also happen on other platforms, like Solaris. + + Examples: + + - ``18:4f:32:5a:64:5`` (note the ``:5`` at the end) + - ``14:cc:20:1a:99:0`` (note the ``:0`` at the end) + """ class Variables(VarsClass): """ - Things that can change. Essentially most of the global variables in getmac. + Things that can change. + Essentially most of the global variables in getmac. """ - #: Get and cache the configured system PATH on import - #: The process environment does not change after a process is started PATH: List[str] = os.environ.get("PATH", os.defpath).split(os.pathsep) + """ + Get and cache the configured system PATH environment variable on import. + The process environment does not change after a process is started. + + :meta hide-value: + """ + PATH_STR: str = os.pathsep.join(PATH) + """ + The :attr:`~getmac.variables.Variables.PATH` as a string. + Used for lookups by :func:`getmac.utils.check_command`, + which uses :func:`shutil.which` under the hood. + + :meta hide-value: + """ - #: Use a copy of the environment so we don't - #: modify the process's current environment. ENV: Dict[str, str] = dict(os.environ) + """ + Use a copy of the environment so any modifications that need to be made + for operation of getmac doesn't modify the process's current environment. + + :meta hide-value: + """ - #: Cache of commands that have been checked for existence by check_command() CHECK_COMMAND_CACHE: Dict[str, bool] = {} + """ + Cache of commands that have been checked for existence by + :func:`~getmac.utils.check_command`. This speeds up subsequent + lookups of the same command. The key is the command name, and + the value is a boolean indicating if it exists. + """ - #: Configure logging - log = logging.getLogger("getmac") # type: logging.Logger + log: logging.Logger = logging.getLogger("getmac") + """ + Global logger for getmac. The logger name is `getmac`. + + :meta hide-value: + """ - #: Default interface DEFAULT_IFACE: str = "" + """ + Name of the local host's default network interface. + """ def __init__(self) -> None: super().__init__() @@ -150,6 +235,6 @@ def __init__(self) -> None: self.PATH_STR = os.pathsep.join(self.PATH) -settings = Settings() -consts = Constants() -gvars = Variables() +settings: Final[Settings] = Settings() +consts: Final[Constants] = Constants() +gvars: Final[Variables] = Variables() From 1724f4f4444556e81575dcc86544019aa17aedd0 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 12 Jul 2025 14:21:31 -0600 Subject: [PATCH 080/112] chore: fix lint issues Someday I'll setup a pre-commit hook...perhaps the same day I ditch Windoze for good. --- getmac/utils.py | 8 ++++++-- pyproject.toml | 5 ++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/getmac/utils.py b/getmac/utils.py index 369eea8..e4ee19e 100644 --- a/getmac/utils.py +++ b/getmac/utils.py @@ -27,7 +27,9 @@ def check_command(command: str) -> bool: If the command exists """ if command not in gvars.CHECK_COMMAND_CACHE: - gvars.CHECK_COMMAND_CACHE[command] = bool(shutil.which(command, path=gvars.PATH_STR)) + gvars.CHECK_COMMAND_CACHE[command] = bool( + shutil.which(command, path=gvars.PATH_STR) + ) return gvars.CHECK_COMMAND_CACHE[command] @@ -216,7 +218,9 @@ def call_proc(executable: str, args: str) -> str: else: cmd = [executable, *shlex.split(args)] # type: ignore - output: Union[str, bytes] = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, env=gvars.ENV) + output: Union[str, bytes] = subprocess.check_output( + cmd, stderr=subprocess.DEVNULL, env=gvars.ENV + ) if settings.DEBUG >= 4: gvars.log.debug(f"Output from '{executable}' command: {output!s}") diff --git a/pyproject.toml b/pyproject.toml index 46d07f1..75e4c83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -202,7 +202,7 @@ addopts = "--tb=short" # --- Codespell configuration --- [tool.codespell] check-filenames = true -skip = '*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,*.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist' +skip = '*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist,built_docs,.pytest_cache,.pdm-build' ignore-words-list = 'ether,Expresso,expresso,Espresso,espresso' @@ -262,8 +262,6 @@ select = [ # Linting error codes to ignore ignore = [ - "ANN101", # missing-type-self - "ANN102", # missing-type-cls "RET505", # unnecessary 'else' after 'return' statement "PLW0603", # Using the global statement to update is discouraged "PLW2901", # for loop variable overwritten by assignment target @@ -273,6 +271,7 @@ ignore = [ # "PLC1901", # compare-to-empty-string "RUF012", # Mutable class attributes should be annotated with typing.ClassVar "S603", # subprocess-without-shell-equals-true + "PLC0415", # import should be at the top-level of a file (getmac uses imports in functions) ] [tool.ruff.lint.per-file-ignores] From 8db0383d6f5a9e886edc724c326eb4e2ab707d26 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 12 Jul 2025 14:41:25 -0600 Subject: [PATCH 081/112] style: sort imports using isort --- getmac/__init__.py | 2 +- getmac/__main__.py | 2 +- getmac/getmac.py | 2 +- getmac/utils.py | 4 +-- pdm.lock | 59 ++++++++++++++++++++++++++------------------ pyproject.toml | 16 +++++++----- tests/test_getmac.py | 2 +- 7 files changed, 51 insertions(+), 36 deletions(-) diff --git a/getmac/__init__.py b/getmac/__init__.py index 490550b..ed47bfc 100644 --- a/getmac/__init__.py +++ b/getmac/__init__.py @@ -1,3 +1,3 @@ -from .getmac import __version__, settings, get_mac_address # noqa: F401 +from .getmac import __version__, get_mac_address, settings # noqa: F401 __all__ = ["get_mac_address"] diff --git a/getmac/__main__.py b/getmac/__main__.py index 91f86dd..2852524 100644 --- a/getmac/__main__.py +++ b/getmac/__main__.py @@ -5,7 +5,7 @@ import sys from . import getmac -from .variables import settings, gvars +from .variables import gvars, settings def build_parser() -> argparse.ArgumentParser: diff --git a/getmac/getmac.py b/getmac/getmac.py index 8ba5478..0684401 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -40,7 +40,7 @@ from typing import Dict, Final, List, Optional, Set, Tuple, Type, Union from . import utils -from .variables import settings, consts, gvars +from .variables import consts, gvars, settings #: Current version of getmac package __version__ = "1.0.0a0" diff --git a/getmac/utils.py b/getmac/utils.py index e4ee19e..b093065 100644 --- a/getmac/utils.py +++ b/getmac/utils.py @@ -7,12 +7,12 @@ import os import re import shlex +import shutil import socket import subprocess -import shutil from typing import Optional, Union -from .variables import settings, consts, gvars +from .variables import consts, gvars, settings def check_command(command: str) -> bool: diff --git a/pdm.lock b/pdm.lock index 1c9bed2..473743f 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "docs", "lint", "test"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:42c906fbb87a3193b72c041b2cedcd4a1e4971c5cb18eae496cf8b969510c5cc" +content_hash = "sha256:631cd6558edfe1775f4a1193c477bb813c2946e5a10e572cd321556ee167f084" [[metadata.targets]] requires_python = "~=3.8" @@ -55,7 +55,7 @@ files = [ [[package]] name = "black" -version = "23.12.1" +version = "24.8.0" requires_python = ">=3.8" summary = "The uncompromising code formatter." groups = ["lint"] @@ -69,28 +69,28 @@ dependencies = [ "typing-extensions>=4.0.1; python_version < \"3.11\"", ] files = [ - {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, - {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, - {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, - {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, - {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, - {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, - {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, - {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, - {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, - {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, - {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, - {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, - {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, - {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, - {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, - {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, - {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, - {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, - {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, - {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, - {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, - {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, + {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, + {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, + {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, + {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, + {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, + {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, + {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, + {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, + {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, + {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, + {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, + {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, + {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, + {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, + {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, + {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, + {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, + {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, + {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, + {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, + {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, + {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, ] [[package]] @@ -521,6 +521,17 @@ files = [ {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] +[[package]] +name = "isort" +version = "5.13.2" +requires_python = ">=3.8.0" +summary = "A Python utility / library to sort Python imports." +groups = ["lint"] +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + [[package]] name = "jinja2" version = "3.1.6" diff --git a/pyproject.toml b/pyproject.toml index 75e4c83..e5bfa69 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,18 +4,17 @@ build-backend = "pdm.backend" [project] name = "getmac" -dynamic = ["version"] # , "readme" +dynamic = ["version"] description = "Platform-independent pure-Python module to get a MAC address" dependencies = [] readme = "README.md" -# readme = { file = "README.md", content-type = "text/markdown" } authors = [ {name = "Christopher Goes", email = "ghostofgoes@gmail.com"}, ] maintainers = [ {name = "Christopher Goes", email = "ghostofgoes@gmail.com"}, ] -license = { file = "LICENSE"} # text = "MIT" +license = { file = "LICENSE"} requires-python = "<4.0,>=3.8" classifiers = [ "Development Status :: 5 - Production/Stable", @@ -89,14 +88,16 @@ test = [ "pytest-xdist", "psutil", "pygments<3.0,>=2.13", + # TODO: pytest-sugar ] lint = [ "codespell>=2.2.4", "tomli; python_version < \"3.12\"", "ruff", "vulture", - "black<24.0.0,>=23.3.0", + "black", "mypy<2.0.0,>=1.3.0", + "isort", ] docs = [ "furo; python_version >= \"3.10\"", @@ -145,8 +146,8 @@ cmd = "vulture --min-confidence 100 getmac tests" [tool.pdm.scripts.check_format] help = "Check code formatting is correct" composite = [ + "isort --check getmac tests", "black --check getmac tests", - # TODO: isort ] [tool.pdm.scripts.typing] @@ -166,8 +167,8 @@ composite = [ [tool.pdm.scripts.format] help = "Auto-format code to match requirements" composite = [ + "isort getmac tests", "black getmac tests", - # TODO: isort ] [tool.pdm.scripts.benchmark] @@ -276,3 +277,6 @@ ignore = [ [tool.ruff.lint.per-file-ignores] "tests/*.py" = ["ANN001", "ANN002", "ANN003", "ANN201", "ANN202", "S101", "S104", "S603"] + +[tool.isort] +profile = "black" diff --git a/tests/test_getmac.py b/tests/test_getmac.py index e387836..94173a6 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -12,7 +12,7 @@ import pytest from getmac import get_mac_address, getmac, utils -from getmac.variables import settings, consts, gvars +from getmac.variables import consts, gvars, settings def test_all_methods_defined_are_in_methods_list(): From 7e4fadf17e5376c5c6bc8e8ec7e5619f10e540b5 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 Jul 2025 18:59:54 -0600 Subject: [PATCH 082/112] lint and test improvements --- CHANGELOG.md | 5 ++++- docs/misc_docs/TODO.md | 10 ++++----- getmac/getmac.py | 48 +++++++++++++++++++++--------------------- getmac/utils.py | 2 +- getmac/variables.py | 1 + pdm.lock | 28 +++++++++++++++++++++++- pyproject.toml | 24 +++++++++++++-------- tests/conftest.py | 5 +---- 8 files changed, 78 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fabfcb5..f110d2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,8 @@ - If you pass it some weird bytes that aren't decodable as utf-8, it's obviously going to break. Don't be dumb :) ### Changed -* Reduce size of wheel file (`.whl`) +* BREAKING CHANGE: refactored how settings are handled. Instead of module-level globals, they're implemented in a Settings singleton in `getmac.settings`. For example, `getmac.getmac.PORT` should now be `getmac.settings.PORT`. +* Reduce size of wheel distribution (`.whl` file) ### Removed * Removed support for Python 2.7. It's time. Supporting 2.7 has become an onerous burden on the project, and has prevented fully supporting 3.10 and 3.11 due to having to stay on an ancient version of pytest. I'm done supporting a version of the language that hasn't been updated in nearly 15 years, and has been wholly unsupported for over 5 years. @@ -36,6 +37,8 @@ * Add `pyproject.toml`, consolidated most tool configurations here * Removed `setup.py`, `MANIFEST.in`, `requirements*.txt`, `tox.ini` * Switched to CodeCov from Coveralls +* Use [isort](https://pycqa.github.io/isort/index.html) to sort imports +* Refactored source code documentation and added an API reference to the docs ## 0.9.5 (07/15/2024) diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index 0ee9405..34375b6 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -10,7 +10,7 @@ - [ ] Re-add Man pages (and auto-build them in CI and include in releases and the distributions) - [x] Document `get_by_method()` - [x] Document `initialize_method_cache()` -- [ ] Auto-generated API docs +- [x] Auto-generated API docs - [x] Add docstrings to all util methods - [x] Furo, sphinx-autodoc-typehints, sphinx-argparse-cli, sphinx-automodapi, sphinx-copybutton, recommonmark @@ -19,7 +19,7 @@ - [ ] >90% test coverage - refactor tests to use the new system and structure - directly test methods via a `Method.parse()` function - - add `Method.parse()` that handles the parsing of command + - add `Method.parse()` that handles the parsing of command - [ ] Improve CLI tests to ensure output is what's expected (e.g. ensure `--override-port` logs a warning and the value actually gets overridden) ## Features @@ -43,7 +43,7 @@ - Passing an instance of a subclass of `getmac.Method` - Add a CLI argument to reference class by name - Add ability to exclude methods. Just remove them from METHODS list so they never get used. Useful for testing specific methods or working around buggy methods. - - Document these features in the README/docs, including the CLI arguments + - Document these features in the README/docs, including the CLI arguments ## Enhancements/fixes/misc. - [ ] Properly support WSL2 @@ -92,9 +92,9 @@ Removing Tox, maybe. Can do version enumeration in GitHub Actions, for all platf # Etc - [ ] Add to Conda Forge ([example here](https://github.com/conda-forge/staged-recipes/pull/26828/files)) -- [ ] Add [isort](https://pycqa.github.io/isort/) (requires python 3.8+) +- [x] Add [isort](https://pycqa.github.io/isort/) (requires python 3.8+) - [ ] cache the result of executable checks in `getmac.utils.popen()` -- [ ] Refactor the default interface code. Combine the functions into +- [ ] Refactor the default interface code. Combine the functions into one, move the default fallback logic into the function. - TODO: MAC -> IP. "to_find='mac'"? (create GitHub issue?) - [ ] Move method classes into a separate file diff --git a/getmac/getmac.py b/getmac/getmac.py index 0684401..a35e571 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -1763,35 +1763,35 @@ def get_mac_address( mac = get_by_method("ip4", ip) elif interface: mac = get_by_method("iface", interface) - else: # Default to searching for interface + # === Default to searching for interface === + elif consts.WINDOWS and network_request: # Default to finding MAC of the interface with the default route - if consts.WINDOWS and network_request: - default_iface_ip = utils.fetch_ip_using_dns() - mac = get_by_method("ip4", default_iface_ip) - elif consts.WINDOWS: - # TODO: implement proper default interface detection on windows - # (add a Method subclass to implement DefaultIface on Windows) - mac = get_by_method("iface", "Ethernet") - else: - if not gvars.DEFAULT_IFACE: - gvars.DEFAULT_IFACE = get_by_method("default_iface") # type: ignore + default_iface_ip = utils.fetch_ip_using_dns() + mac = get_by_method("ip4", default_iface_ip) + elif consts.WINDOWS: + # TODO: implement proper default interface detection on windows + # (add a Method subclass to implement DefaultIface on Windows) + mac = get_by_method("iface", "Ethernet") + else: + if not gvars.DEFAULT_IFACE: + gvars.DEFAULT_IFACE = get_by_method("default_iface") # type: ignore - if gvars.DEFAULT_IFACE: - gvars.DEFAULT_IFACE = str(gvars.DEFAULT_IFACE).strip() + if gvars.DEFAULT_IFACE: + gvars.DEFAULT_IFACE = str(gvars.DEFAULT_IFACE).strip() - # TODO: better fallback if default iface lookup fails - if not gvars.DEFAULT_IFACE and consts.BSD: - gvars.DEFAULT_IFACE = "em0" - elif not gvars.DEFAULT_IFACE and consts.DARWIN: # OSX, maybe? - gvars.DEFAULT_IFACE = "en0" - elif not gvars.DEFAULT_IFACE: - gvars.DEFAULT_IFACE = "eth0" + # TODO: better fallback if default iface lookup fails + if not gvars.DEFAULT_IFACE and consts.BSD: + gvars.DEFAULT_IFACE = "em0" + elif not gvars.DEFAULT_IFACE and consts.DARWIN: # OSX, maybe? + gvars.DEFAULT_IFACE = "en0" + elif not gvars.DEFAULT_IFACE: + gvars.DEFAULT_IFACE = "eth0" - mac = get_by_method("iface", gvars.DEFAULT_IFACE) + mac = get_by_method("iface", gvars.DEFAULT_IFACE) - # TODO: hack to fallback to loopback if lookup fails - if not mac: - mac = get_by_method("iface", "lo") + # TODO: hack to fallback to loopback if lookup fails + if not mac: + mac = get_by_method("iface", "lo") gvars.log.debug(f"Raw MAC found: {mac}") diff --git a/getmac/utils.py b/getmac/utils.py index b093065..e8791eb 100644 --- a/getmac/utils.py +++ b/getmac/utils.py @@ -246,7 +246,7 @@ def uuid_convert(mac: int) -> str: def fetch_ip_using_dns() -> str: """ - Determines the IP address of the default network interface. + Determine the IP address of the default network interface. Sends a UDP packet to Cloudflare's DNS (``1.1.1.1``), which should go through the default interface. This populates the source address of the socket, diff --git a/getmac/variables.py b/getmac/variables.py index 74f4918..b52f84d 100644 --- a/getmac/variables.py +++ b/getmac/variables.py @@ -156,6 +156,7 @@ class Constants(VarsClass): class Variables(VarsClass): """ Things that can change. + Essentially most of the global variables in getmac. """ diff --git a/pdm.lock b/pdm.lock index 473743f..cfea5d5 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "docs", "lint", "test"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:631cd6558edfe1775f4a1193c477bb813c2946e5a10e572cd321556ee167f084" +content_hash = "sha256:dd5306c4590f39809504f8d83b88495d8af6962a85e0d0f63238fcc1dab8bd6c" [[metadata.targets]] requires_python = "~=3.8" @@ -844,6 +844,21 @@ files = [ {file = "pytest_randomly-3.15.0.tar.gz", hash = "sha256:b908529648667ba5e54723088edd6f82252f540cc340d748d1fa985539687047"}, ] +[[package]] +name = "pytest-sugar" +version = "1.0.0" +summary = "pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly)." +groups = ["test"] +dependencies = [ + "packaging>=21.3", + "pytest>=6.2.0", + "termcolor>=2.1.0", +] +files = [ + {file = "pytest-sugar-1.0.0.tar.gz", hash = "sha256:6422e83258f5b0c04ce7c632176c7732cab5fdb909cb39cca5c9139f81276c0a"}, + {file = "pytest_sugar-1.0.0-py3-none-any.whl", hash = "sha256:70ebcd8fc5795dc457ff8b69d266a4e2e8a74ae0c3edc749381c64b5246c8dfd"}, +] + [[package]] name = "pytest-xdist" version = "3.6.1" @@ -1153,6 +1168,17 @@ files = [ {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, ] +[[package]] +name = "termcolor" +version = "2.4.0" +requires_python = ">=3.8" +summary = "ANSI color formatting for output in terminal" +groups = ["test"] +files = [ + {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, + {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, +] + [[package]] name = "tomli" version = "2.2.1" diff --git a/pyproject.toml b/pyproject.toml index e5bfa69..0c82601 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,9 +86,9 @@ test = [ "pytest-benchmark", "pytest-cov", "pytest-xdist", + "pytest-sugar", "psutil", "pygments<3.0,>=2.13", - # TODO: pytest-sugar ] lint = [ "codespell>=2.2.4", @@ -234,10 +234,6 @@ extend-exclude = [ # If you're still confused, ask a fellow developer for assistance. # You can also run "ruff rule " to explain a rule on the command line, without a browser or internet access. select = [ - "F", # Pyflakes - "E", # pycodestyle - "W", # Warning - "N", # pep8-naming "ANN", # flake8-annotations "S", # flake8-bandit "B", # flake8-bugbear @@ -254,11 +250,18 @@ select = [ "RET", # flake8-return "TID", # flake8-tidy-imports "ARG", # flake8-unused-arguments + "N", # pep8-naming + "E", # pycodestyle - Error + "W", # pycodestyle - Warning + "F", # Pyflakes "PGH", # pygrep-hooks - "PLC", # Pylint Convention - "PLE", # Pylint Errors - "PLW", # Pylint Warnings + "PLC", # Pylint - Convention + "PLE", # Pylint - Errors + "PLR", # Pylint - Refactor + "PLW", # Pylint - Warnings + # "UP", # pyupgrade "RUF", # Ruff-specific rules + ] # Linting error codes to ignore @@ -273,10 +276,13 @@ ignore = [ "RUF012", # Mutable class attributes should be annotated with typing.ClassVar "S603", # subprocess-without-shell-equals-true "PLC0415", # import should be at the top-level of a file (getmac uses imports in functions) + "PLR0915", + "PLR0912", + "PLR2004", ] [tool.ruff.lint.per-file-ignores] -"tests/*.py" = ["ANN001", "ANN002", "ANN003", "ANN201", "ANN202", "S101", "S104", "S603"] +"tests/*.py" = ["ANN001", "ANN002", "ANN003", "ANN201", "ANN202", "S101", "S104", "S603", "D", "PLR"] [tool.isort] profile = "black" diff --git a/tests/conftest.py b/tests/conftest.py index 364da43..e9a0a39 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,3 @@ -import io from os import path import pytest @@ -12,9 +11,7 @@ def get_sample(): def _get_sample(sample_path): sdir = path.realpath(path.join(path.dirname(__file__), "samples")) - with io.open( - path.join(sdir, sample_path), "rt", newline="", encoding="utf-8" - ) as f: + with open(path.join(sdir, sample_path), newline="", encoding="utf-8") as f: return f.read() return _get_sample From 1798f4d177aa04e16dff2ab201f8f96c98fbadab Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 16:32:30 -0700 Subject: [PATCH 083/112] dev: add PDM scripts for manpage and doc build --- pyproject.toml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0c82601..687a60c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -189,8 +189,21 @@ pytest -n auto """ +[tool.pdm.scripts.html] +help = "Build HTML documentation using Sphinx" +cmd = "sphinx-build -j auto -b html ./docs ./built_docs" + +[tool.pdm.scripts.manpage] +help = "Build Manpage documentation using Sphinx" +cmd = "sphinx-build -b man ./docs ./built_docs" + [tool.pdm.scripts.docs] -cmd = "sphinx-build ./docs ./built_docs" +help = "Build all documentation formats using Sphinx" +composite = [ + "html", + "manpage", +] + # --- Pytest configuration --- From 63ec2927c72bae95db2a436ba8c3fe2bcabc1bd8 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 16:32:58 -0700 Subject: [PATCH 084/112] doc: build manpage from CLI docs --- docs/conf.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a3a6314..8d868bd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -44,9 +44,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -# TODO: auto-build and include the man page in the release archive man_pages = [ - ('index', 'getmac', 'Cross-platform Python package to get MAC addresses', + ('cli', 'getmac', 'Cross-platform Python package to get MAC addresses', [author], 1) ] From 06f2c52c46b812a53bfd6ffd369ab8fc6638e13c Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 16:45:03 -0700 Subject: [PATCH 085/112] ci: add doc build to CI --- .github/workflows/ci.yml | 36 ++++++++++++++++++++++++++++++++++++ docs/misc_docs/TODO.md | 1 + pyproject.toml | 3 +-- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccfb692..e9d18a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,6 +76,42 @@ jobs: with: fail_ci_if_error: false token: ${{ secrets.CODECOV_TOKEN }} + html-docs: + name: Build HTML documentation + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Setup PDM + uses: pdm-project/setup-pdm@v4 + with: + python-version: "3.11" + cache: true + - name: Install dependencies + run: pdm sync -d -G docs + - name: Build HTML + run: pdm run sphinx-build -b html docs docs/_build/html + - uses: actions/upload-artifact@v4 + with: + name: html-docs + path: docs/_build/html + manpage-docs: + name: Build manpage + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Setup PDM + uses: pdm-project/setup-pdm@v4 + with: + python-version: "3.11" + cache: true + - name: Install dependencies + run: pdm sync -d -G docs + - name: Build manpage + run: pdm run sphinx-build -b man docs docs/_build/man + - uses: actions/upload-artifact@v4 + with: + name: manpage + path: docs/_build/man/getmac.1 # TODO: publish releases using CI diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index 34375b6..29f7f3c 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -46,6 +46,7 @@ - Document these features in the README/docs, including the CLI arguments ## Enhancements/fixes/misc. +- [ ] Python 3.13 + 3.14 - [ ] Properly support WSL2 - [ ] address all TODOs in the code - [ ] implement proper default interface detection on Windows diff --git a/pyproject.toml b/pyproject.toml index 687a60c..aa6c12a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ classifiers = [ "Topic :: Utilities", "Typing :: Typed", ] -keywords=[ +keywords = [ "getmac", "get-mac", "macaddress", @@ -57,7 +57,6 @@ keywords=[ "mac", "ethernet", "mac-48", - "networking", "network", "networking", "layer2", From ce33ec029c11ad73c74da48615ec54ff0c7095af Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 16:46:28 -0700 Subject: [PATCH 086/112] build: CI updates --- .github/workflows/ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9d18a9..9480c33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: name: Run lint checks runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup PDM uses: pdm-project/setup-pdm@v4 with: @@ -40,7 +40,7 @@ jobs: - x86 - x64 platform: - - ubuntu-20.04 + - ubuntu-22.04 - windows-latest # Fix tests failing on MacOS 14+ due to ARM architecture # https://github.com/actions/setup-python/issues/825 @@ -48,12 +48,12 @@ jobs: # architecture to the list of architectures above. - macos-13 exclude: - - platform: ubuntu-20.04 + - platform: ubuntu-22.04 architecture: x86 - platform: macos-13 architecture: x86 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup PDM uses: pdm-project/setup-pdm@v4 with: @@ -80,7 +80,7 @@ jobs: name: Build HTML documentation runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup PDM uses: pdm-project/setup-pdm@v4 with: @@ -98,7 +98,7 @@ jobs: name: Build manpage runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup PDM uses: pdm-project/setup-pdm@v4 with: @@ -132,7 +132,7 @@ jobs: # # IMPORTANT: this permission is mandatory for trusted publishing # id-token: write # steps: -# - uses: actions/checkout@v4 +# - uses: actions/checkout@v6 # - name: Setup PDM # uses: pdm-project/setup-pdm@v4 # with: From 93b0403ccb99264986167fc71ee8fad16cc090e0 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 16:53:46 -0700 Subject: [PATCH 087/112] test: fix bad mocks --- tests/test_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 623a912..681dc74 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -41,16 +41,16 @@ def test_search(get_sample): def test_call_proc(mocker): - mocker.patch("getmac.utils.DEVNULL", "DEVNULL") + mocker.patch("subprocess.DEVNULL", "DEVNULL") mocker.patch.object(gvars, "ENV", "ENV") mocker.patch.object(consts, "WINDOWS", True) - m = mocker.patch("getmac.utils.check_output", return_value="WINSUCCESS") + m = mocker.patch("subprocess.check_output", return_value="WINSUCCESS") assert utils.call_proc("CMD", "arg") == "WINSUCCESS" m.assert_called_once_with("CMD arg", stderr="DEVNULL", env="ENV") mocker.patch.object(consts, "WINDOWS", False) - m = mocker.patch("getmac.utils.check_output", return_value="YAY") + m = mocker.patch("subprocess.check_output", return_value="YAY") assert utils.call_proc("CMD", "arg1 arg2") == "YAY" m.assert_called_once_with(["CMD", "arg1", "arg2"], stderr="DEVNULL", env="ENV") From 81a40da90b10cdecf0a7ad33ff135f8f25aee4ec Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 17:02:11 -0700 Subject: [PATCH 088/112] ci: attempt to fix MacOS tests --- .github/workflows/ci.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9480c33..b43a5e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,19 +39,22 @@ jobs: architecture: - x86 - x64 + - arm64 platform: - ubuntu-22.04 - windows-latest - # Fix tests failing on MacOS 14+ due to ARM architecture - # https://github.com/actions/setup-python/issues/825 - # TODO: update tests to use ARM MacOS with 14+, add - # architecture to the list of architectures above. - - macos-13 + - macos-latest exclude: - platform: ubuntu-22.04 architecture: x86 - - platform: macos-13 + - platform: ubuntu-22.04 + architecture: arm64 + - platform: windows-latest + architecture: arm64 + - platform: macos-latest architecture: x86 + - platform: macos-latest + architecture: x64 steps: - uses: actions/checkout@v6 - name: Setup PDM From cdfe236dae2882b6a43a90760a29ca79473ad0e6 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 17:12:27 -0700 Subject: [PATCH 089/112] simplify doc CI --- .github/workflows/ci.yml | 33 +++++++++------------------------ .gitignore | 2 ++ pyproject.toml | 4 ++-- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b43a5e8..f131d95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: jobs: lint: name: Run lint checks - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 - name: Setup PDM @@ -79,8 +79,8 @@ jobs: with: fail_ci_if_error: false token: ${{ secrets.CODECOV_TOKEN }} - html-docs: - name: Build HTML documentation + docs: + name: Build HTML docs and manpage runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 @@ -91,31 +91,16 @@ jobs: cache: true - name: Install dependencies run: pdm sync -d -G docs - - name: Build HTML - run: pdm run sphinx-build -b html docs docs/_build/html - - uses: actions/upload-artifact@v4 + - name: Build docs + run: pdm run docs + - uses: actions/upload-artifact@v6 with: name: html-docs - path: docs/_build/html - manpage-docs: - name: Build manpage - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v6 - - name: Setup PDM - uses: pdm-project/setup-pdm@v4 - with: - python-version: "3.11" - cache: true - - name: Install dependencies - run: pdm sync -d -G docs - - name: Build manpage - run: pdm run sphinx-build -b man docs docs/_build/man - - uses: actions/upload-artifact@v4 + path: html_docs/ + - uses: actions/upload-artifact@v6 with: name: manpage - path: docs/_build/man/getmac.1 - + path: manpage/getmac.1 # TODO: publish releases using CI # TODO: don't publish pre-releases OR publish them as pre-releases to PyPI diff --git a/.gitignore b/.gitignore index 9a30972..741f83e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ .pdm-python built_docs/ +html_docs/ +manpage/ docs/api/ .pytest_cache diff --git a/pyproject.toml b/pyproject.toml index aa6c12a..ef8260b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -190,11 +190,11 @@ pytest [tool.pdm.scripts.html] help = "Build HTML documentation using Sphinx" -cmd = "sphinx-build -j auto -b html ./docs ./built_docs" +cmd = "sphinx-build -j auto -b html ./docs ./html_docs" [tool.pdm.scripts.manpage] help = "Build Manpage documentation using Sphinx" -cmd = "sphinx-build -b man ./docs ./built_docs" +cmd = "sphinx-build -j auto -b man ./docs ./manpage" [tool.pdm.scripts.docs] help = "Build all documentation formats using Sphinx" From feee442cb06062af54d328e4a8d97731cb31946f Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 17:30:56 -0700 Subject: [PATCH 090/112] ci: publish docs to github pages --- .github/workflows/ci.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f131d95..1e85a69 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,10 +97,28 @@ jobs: with: name: html-docs path: html_docs/ + - uses: actions/upload-pages-artifact@v4 + with: + path: html_docs/ - uses: actions/upload-artifact@v6 with: name: manpage path: manpage/getmac.1 + deploy: + name: Publish docs to GitHub Pages + needs: docs + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/1.0.0-wip' + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 # TODO: publish releases using CI # TODO: don't publish pre-releases OR publish them as pre-releases to PyPI From 2a979ad84246baaf0a1ea875b53847b7266e672d Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 17:43:22 -0700 Subject: [PATCH 091/112] build: add documentation link --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ef8260b..daff559 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,8 +66,7 @@ keywords = [ [project.urls] "Homepage" = "https://github.com/GhostofGoes/getmac" -# TODO: get ReadTheDocs working -# "Documentation" = "https://getmac.readthedocs.io/en/latest/" +"Documentation" = "https://ghostofgoes.github.io/getmac/" "Changelog" = "https://github.com/GhostofGoes/getmac/blob/main/CHANGELOG.md" "Issue tracker" = "https://github.com/GhostofGoes/getmac/issues" "Source" = "https://github.com/GhostofGoes/getmac" From fba638d071437d44400e3a131d2d346bd8c91ffa Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 18:04:23 -0700 Subject: [PATCH 092/112] docs: add changelog and contributor pages --- CONTRIBUTING.md | 29 ++++---- docs/changelog.rst | 2 + docs/code_of_conduct.rst | 2 + docs/conf.py | 1 + docs/contributing.rst | 2 + docs/index.rst | 4 ++ docs/misc_docs/TODO.md | 5 +- docs/security.rst | 2 + docs/usage.rst | 2 +- pdm.lock | 147 ++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 11 files changed, 179 insertions(+), 18 deletions(-) create mode 100644 docs/changelog.rst create mode 100644 docs/code_of_conduct.rst create mode 100644 docs/contributing.rst create mode 100644 docs/security.rst diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ecb96a3..f47d299 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ Thanks for taking an interest in this awesome little project. We love to bring n * Discussion and general questions/help: [GitHub discussions](https://github.com/GhostofGoes/getmac/discussions) or the [Python Discord server](https://discord.gg/python) -# Code requirements +## Code requirements Your code *must*: * Have tests @@ -36,22 +36,22 @@ Please be respectful and follow the [Code of Conduct](CODE_OF_CONDUCT.md). Memes * [ ] Coverage has NOT decreased -# Where to contribute +## Where to contribute -## Good for beginners +### Good for beginners * Sample collection (see section below) * Platform testing (see section below) * Bug reports! * Documentation (including fixes for grammar and spelling) * Improving and adding tests for existing samples -## Main areas of focus +### Main areas of focus * Writing parsers for new commands (Example: `netsh int ipv6`) * Addressing missing functionality (Example: default interface detection for IPv6 on Windows) * Adding new features (Example: ability to find MAC by interface index integer) * Adding tests for internal methods and mocking where necessary -## Platform testing +### Platform testing Help is dearly needed on testing and rooting out differences in various platforms and configurations. At a basic level, this involves just running the tests on any platforms you use. Open issues for any bugs or quirks you discover, or if you're feeling adventurous, fix it yourself! **Any platform is fair game!** The following are platforms of special interest: @@ -61,14 +61,14 @@ Help is dearly needed on testing and rooting out differences in various platform * Arch Linux * BSDs -## Sample collection +### Sample collection Examples of output of various commands is an easy way contribute that is still incredibly helpful. 1. Run the command 2. Copy/paste the output (or redirect output of command, `tee` is helpful here) into an appropriately named `.out` file in `samples/` 3. That's it! -# Getting started +## Getting started 1. Create your own fork of the code through GitHub web interface ([Here's a Guide](https://gist.github.com/Chaser324/ce0505fbed06b947d962)) 1. Clone the fork to your computer. This can be done using the [GitHub desktop](https://desktop.github.com/) GUI , `git clone `, or the Git tools in your favorite editor or IDE. 1. Create and checkout a new branch in the fork with either your username (e.g. "ghostofgoes"), or the name of the feature or issue you're working on (e.g. "openbsd-support"). Again, this can be done using the GUI, your favorite editor, or `git checkout -b origin/`. @@ -91,7 +91,7 @@ Examples of output of various commands is an easy way contribute that is still i 8. Submit a pull request! -# Bug reports +## Bug reports Filing a bug report: 1. Answer these questions: @@ -107,13 +107,13 @@ Filing a bug report: **NOTE**: If the issue is a potential security vulnerability, do *NOT* open an issue! Instead, email: ghostofgoes(at)gmail(dot)com -# Features and ideas +## Features and ideas Ideas for features or other things are welcomed. Open an issue on GitHub detailing the idea, and tag it appropriately (e.g. "Feature" for a new feature). -# Resources +## Resources -## Regex resources (regular expressions) +### Regex resources (regular expressions) - https://pythex.org/ - https://regex101.com/ - [Python's `re` documentation](https://docs.python.org/3/library/re.html) @@ -121,7 +121,7 @@ Ideas for features or other things are welcomed. Open an issue on GitHub detaili - https://ultrapico.com/Expresso.htm (I haven't used this but it looks useful) -# Commands +## Commands ```bash # Create development environment pdm install -d @@ -136,10 +136,11 @@ pdm run test pdm run lint # Run getmac CLI -pdm run getmac +pdm run getmac --help +pdm run getmac --version ``` -# Documentation +## Documentation The docs are built using Sphinx. They are located in the `docs/` folder, and the configuration is in `docs/conf.py`. diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 0000000..4afc7d3 --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,2 @@ +.. include:: ../CHANGELOG.md + :parser: myst_parser.sphinx_ diff --git a/docs/code_of_conduct.rst b/docs/code_of_conduct.rst new file mode 100644 index 0000000..43478f4 --- /dev/null +++ b/docs/code_of_conduct.rst @@ -0,0 +1,2 @@ +.. include:: ../CODE_OF_CONDUCT.md + :parser: myst_parser.sphinx_ diff --git a/docs/conf.py b/docs/conf.py index 8d868bd..f22bc3e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,6 +27,7 @@ 'sphinx_argparse_cli', # Adds CLI documentation from argparse 'sphinx_automodapi.automodapi', # API documentation 'sphinx_issues', # GitHub Issues/PRs - :issue:, :pr: + 'myst_parser', # Markdown support ] intersphinx_mapping = { diff --git a/docs/contributing.rst b/docs/contributing.rst new file mode 100644 index 0000000..6889b7c --- /dev/null +++ b/docs/contributing.rst @@ -0,0 +1,2 @@ +.. include:: ../CONTRIBUTING.md + :parser: myst_parser.sphinx_ diff --git a/docs/index.rst b/docs/index.rst index 1668724..f6cc257 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,3 +15,7 @@ It provides a Python function, :func:`~getmac.getmac.get_mac_address`, and a com usage cli api + changelog + contributing + security + code_of_conduct diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index 29f7f3c..b3bcff4 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -3,11 +3,12 @@ ## Documentation - [ ] Add guide on using the modules API, e.g. registering a new method in `getmac.getmac.METHODS`, etc. -- [ ] Single page on RTD/publish with GitHub actions built with Sphinx and Furo +- [x] Single page on RTD/publish with GitHub actions built with Sphinx and Furo - [ ] Update docs/usage examples for `get_mac_address()` +- [ ] Include manpage in release - [x] Document possible values for `PLATFORM` variable - [x] Document Method (and subclass) attributes (use Sphinx "#:" comments) -- [ ] Re-add Man pages (and auto-build them in CI and include in releases and the distributions) +- [x] Re-add Man pages (and auto-build them in CI) - [x] Document `get_by_method()` - [x] Document `initialize_method_cache()` - [x] Auto-generated API docs diff --git a/docs/security.rst b/docs/security.rst new file mode 100644 index 0000000..4829f0a --- /dev/null +++ b/docs/security.rst @@ -0,0 +1,2 @@ +.. include:: ../SECURITY.md + :parser: myst_parser.sphinx_ diff --git a/docs/usage.rst b/docs/usage.rst index bd36dc2..11f933d 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -33,7 +33,7 @@ Configuration ============= TODO: update these for new settings classes - +!! - ``logging.getLogger("getmac")``: Runtime messages and errors are recorded to the ``getmac`` logger using Python's :mod:`logging` module. They can be configured by using :func:`logging.basicConfig` or adding :class:`logging.Handler` instances to the logger named ``"getmac"``. diff --git a/pdm.lock b/pdm.lock index cfea5d5..f903085 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "docs", "lint", "test"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:dd5306c4590f39809504f8d83b88495d8af6962a85e0d0f63238fcc1dab8bd6c" +content_hash = "sha256:2da92c01ac895d4f8ad1b998cd69b7d4225b25b05add2e752438520c72ca3938" [[metadata.targets]] requires_python = "~=3.8" @@ -547,6 +547,21 @@ files = [ {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +requires_python = ">=3.8" +summary = "Python port of markdown-it. Markdown parsing, done right!" +groups = ["docs"] +marker = "python_version >= \"3.10\"" +dependencies = [ + "mdurl~=0.1", +] +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + [[package]] name = "markupsafe" version = "3.0.2" @@ -618,6 +633,33 @@ files = [ {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] +[[package]] +name = "mdit-py-plugins" +version = "0.5.0" +requires_python = ">=3.10" +summary = "Collection of plugins for markdown-it-py" +groups = ["docs"] +marker = "python_version >= \"3.10\"" +dependencies = [ + "markdown-it-py<5.0.0,>=2.0.0", +] +files = [ + {file = "mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f"}, + {file = "mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +requires_python = ">=3.7" +summary = "Markdown URL utilities" +groups = ["docs"] +marker = "python_version >= \"3.10\"" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "mypy" version = "1.14.1" @@ -681,6 +723,26 @@ files = [ {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] +[[package]] +name = "myst-parser" +version = "4.0.1" +requires_python = ">=3.10" +summary = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," +groups = ["docs"] +marker = "python_version >= \"3.10\"" +dependencies = [ + "docutils<0.22,>=0.19", + "jinja2", + "markdown-it-py~=3.0", + "mdit-py-plugins>=0.4.1,~=0.4", + "pyyaml", + "sphinx<9,>=7", +] +files = [ + {file = "myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d"}, + {file = "myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4"}, +] + [[package]] name = "packaging" version = "25.0" @@ -874,6 +936,89 @@ files = [ {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, ] +[[package]] +name = "pyyaml" +version = "6.0.3" +requires_python = ">=3.8" +summary = "YAML parser and emitter for Python" +groups = ["docs"] +marker = "python_version >= \"3.10\"" +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + [[package]] name = "recommonmark" version = "0.7.1" diff --git a/pyproject.toml b/pyproject.toml index daff559..1c96315 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,6 +107,7 @@ docs = [ "sphinx-argparse-cli; python_version >= \"3.10\"", "sphinx-automodapi; python_version >= \"3.10\"", "sphinx-issues; python_version >= \"3.10\"", + "myst-parser; python_version >= \"3.10\"", ] [tool.pdm.build] From 884839e63a104b161ea47ca4726a2b23e1f01a8a Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 18:10:03 -0700 Subject: [PATCH 093/112] ci: python 3.13 and 3.14 --- .github/workflows/ci.yml | 2 ++ docs/misc_docs/TODO.md | 1 - docs/misc_docs/releasing.md | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e85a69..9c80672 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,8 @@ jobs: - "3.10" - "3.11" - "3.12" + - "3.13" + - "3.14" architecture: - x86 - x64 diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index b3bcff4..3a3c4e2 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -5,7 +5,6 @@ - [ ] Add guide on using the modules API, e.g. registering a new method in `getmac.getmac.METHODS`, etc. - [x] Single page on RTD/publish with GitHub actions built with Sphinx and Furo - [ ] Update docs/usage examples for `get_mac_address()` -- [ ] Include manpage in release - [x] Document possible values for `PLATFORM` variable - [x] Document Method (and subclass) attributes (use Sphinx "#:" comments) - [x] Re-add Man pages (and auto-build them in CI) diff --git a/docs/misc_docs/releasing.md b/docs/misc_docs/releasing.md index c13a557..2eb5f2a 100644 --- a/docs/misc_docs/releasing.md +++ b/docs/misc_docs/releasing.md @@ -23,3 +23,4 @@ pdm publish 1. Create a tagged release on GitHub including: a) The relevant section of the CHANGELOG in the body b) The source and binary wheels + c) The manpage (`getmac.1`) From 7f7a5e2552a24a30e2b58f41c86114a4c740cb7c Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 18:12:47 -0700 Subject: [PATCH 094/112] dev: exclude built docs from codespell --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1c96315..66e0490 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -215,7 +215,7 @@ addopts = "--tb=short" # --- Codespell configuration --- [tool.codespell] check-filenames = true -skip = '*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist,built_docs,.pytest_cache,.pdm-build' +skip = '*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist,built_docs,.pytest_cache,.pdm-build,manpage,html_docs' ignore-words-list = 'ether,Expresso,expresso,Espresso,espresso' From fb6791e2693f43e4f54348a20a62070d83f8c360 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 18:15:05 -0700 Subject: [PATCH 095/112] build: mark python 3.13 and 3.14 as supported --- docs/misc_docs/TODO.md | 2 +- pyproject.toml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index 3a3c4e2..bf3ad25 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -46,7 +46,7 @@ - Document these features in the README/docs, including the CLI arguments ## Enhancements/fixes/misc. -- [ ] Python 3.13 + 3.14 +- [x] Python 3.13 + 3.14 - [ ] Properly support WSL2 - [ ] address all TODOs in the code - [ ] implement proper default interface detection on Windows diff --git a/pyproject.toml b/pyproject.toml index 66e0490..4fdbdc0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,8 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Intended Audience :: Developers", From a92889942de54d571dcb5a87f83f185395ebbe14 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 18:59:00 -0700 Subject: [PATCH 096/112] doc: usage examples, notes, limitations --- README.md | 29 +++++---- docs/misc_docs/TODO.md | 2 +- docs/usage.rst | 132 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 131 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 8291ae2..9ac8059 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ pip install getmac pip install https://github.com/ghostofgoes/getmac/archive/main.tar.gz ``` -### Debian-based distributions +### Debian-based distributions `apt` may have it available, depending on your distro (see packaging status below): ```bash @@ -60,7 +60,8 @@ sudo apt install getmac ## Python examples ```python -from getmac import get_mac_address +from getmac import get_mac_address, settings + eth_mac = get_mac_address(interface="eth0") win_mac = get_mac_address(interface="Ethernet 3") ip_mac = get_mac_address(ip="192.168.0.1") @@ -69,13 +70,11 @@ host_mac = get_mac_address(hostname="localhost") updated_mac = get_mac_address(ip="10.0.0.1", network_request=True) # Enable debugging -from getmac import getmac -getmac.DEBUG = 2 # DEBUG level 2 +settings.DEBUG = 2 # DEBUG level 2 print(getmac.get_mac_address(interface="Ethernet 3")) # Change the UDP port used for updating the ARP table (UDP packet) -from getmac import getmac -getmac.PORT = 44444 # Default is 55555 +settings.PORT = 44444 # Default is 55555 print(getmac.get_mac_address(ip="192.168.0.1", network_request=True)) ``` @@ -135,18 +134,18 @@ getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost - `ip`: IPv4 address of a remote host - `ip6`: IPv6 address of a remote host - `hostname`: Hostname of a remote host -- `network_request`: If an network request should be made to update and populate the ARP/NDP table of remote hosts used to lookup MACs in most circumstances. Disable this if you want to just use what's already in the table, or if you have requirements to prevent network traffic. The network request is a empty UDP packet sent to a high port, `55555` by default. This can be changed by setting `getmac.PORT` to the desired integer value. Additionally, on Windows, this will send a UDP packet to `1.1.1.1:53` to attempt to determine the default interface (Note: the IP is [CloudFlare's DNS server](https://www.cloudflare.com/learning/dns/what-is-1.1.1.1/)). +- `network_request`: If an network request should be made to update and populate the ARP/NDP table of remote hosts used to lookup MACs in most circumstances. Disable this if you want to just use what's already in the table, or if you have requirements to prevent network traffic. The network request is a empty UDP packet sent to a high port, `55555` by default. This can be changed by setting `getmac.variables.Settings.PORT` to the desired integer value. Additionally, on Windows, this will send a UDP packet to `1.1.1.1:53` to attempt to determine the default interface (Note: the IP is [CloudFlare's DNS server](https://www.cloudflare.com/learning/dns/what-is-1.1.1.1/)). ## Configuration - `logging.getLogger("getmac")`: Runtime messages and errors are recorded to the `getmac` logger using Python's [logging](https://docs.python.org/3/library/logging.html) module. They can be configured by using [logging.basicConfig()](https://docs.python.org/3/library/logging.html#logging.basicConfig) or adding handlers to the `"getmac"` logger. -- `getmac.getmac.DEBUG`: integer value that controls debugging output. The higher the value, the more output you get. -- `getmac.getmac.PORT`: UDP port used to populate the ARP/NDP table (see the documentation of the `network_request` argument in `get_mac_address()` for details) -- `getmac.getmac.OVERRIDE_PLATFORM`: Override the platform detection with the given value (e.g. `"linux"`, `"windows"`, `"freebsd"`, etc.'). Any values returned by `platform.system()` are valid. -- `getmac.getmac.FORCE_METHOD`: Force a specific method to be used, e.g. 'IpNeighborShow'. This will be used regardless of it's method type or platform compatibility, and `Method.test()` will NOT be checked! The list of available methods is in `getmac.getmac.METHODS`. +- `getmac.variables.Settings.DEBUG`: integer value that controls debugging output. The higher the value, the more output you get. +- `getmac.variables.Settings.PORT`: UDP port used to populate the ARP/NDP table (see the documentation of the `network_request` argument in `get_mac_address()` for details) +- `getmac.variables.Settings.OVERRIDE_PLATFORM`: Override the platform detection with the given value (e.g. `"linux"`, `"windows"`, `"freebsd"`, etc.'). Any values returned by `platform.system()` are valid. +- `getmac.variables.Settings.FORCE_METHOD`: Force a specific method to be used, e.g. 'IpNeighborShow'. This will be used regardless of it's method type or platform compatibility, and `Method.test()` will NOT be checked! The list of available methods is in `getmac.getmac.METHODS`. ## Features - Pure-Python (no compiled C-extensions required!) -- Python 3.7+ +- Python 3.8+ - Lightweight, with no dependencies and a relatively small package size (no binary extensions) - Supports most platforms with NO binary extensions required! - Supports CPython and [PyPy](https://www.pypy.org/) @@ -158,7 +157,7 @@ getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost - "Remote hosts" refer to hosts in your local layer 2 network, also commonly referred to as a "broadcast domain", "LAN", or "VLAN". As far as I know, there is not a reliable method to get a MAC address for a remote host external to the LAN. If you know any methods otherwise, please [open a GitHub issue](https://github.com/GhostofGoes/getmac/issues) or shoot me an email, I'd love to be wrong about this. - The first four arguments are mutually exclusive. `network_request` does not have any functionality when the `interface` argument is specified, and can be safely set if using in a script. - The physical transport is assumed to be Ethernet (802.3). Others, such as Wi-Fi (802.11), are currently not tested or considered. I plan to address this in the future, and am definitely open to pull requests or issues related to this, including error reports. -- **Exceptions will be handled silently and returned as a None.** If you run into problems, you can set `DEBUG` to true and get more information about what's happening. If you're still having issues, please create an [issue on GitHub](https://github.com/GhostofGoes/getmac/issues) and include the output with `DEBUG` enabled. +- **Exceptions will be handled silently and returned as a None.** If you run into problems, you can set `DEBUG` to true and get more information about what's happening. If you're still having issues, please create an [issue on GitHub](https://github.com/GhostofGoes/getmac/issues) and include the output with `DEBUG` enabled. ## Commands and techniques by platform - Windows @@ -189,14 +188,14 @@ All or almost all features should work on "supported" platforms. While other ver - Windows - Desktop: 7, 8, 8.1, 10, 11 (thanks @StevenLooman for testing Windows 11!) - - Server: TBD + - Server: 2025, 2022 - Partially supported (untested): 2000, XP, Vista - Linux distros - CentOS/RHEL 6+ - Ubuntu 16.04+ (15.10 and older should work, but are untested) - Fedora (24+) - Mac OSX (Darwin) - - The latest two versions probably (TBD) + - The latest release, though OSX 14 and 13 have been tested with success. - Android (6+) - Windows Subsystem for Linux (WSL) - FreeBSD (11+) diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index bf3ad25..18f5a51 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -4,7 +4,7 @@ ## Documentation - [ ] Add guide on using the modules API, e.g. registering a new method in `getmac.getmac.METHODS`, etc. - [x] Single page on RTD/publish with GitHub actions built with Sphinx and Furo -- [ ] Update docs/usage examples for `get_mac_address()` +- [x] Update docs/usage examples for `get_mac_address()` - [x] Document possible values for `PLATFORM` variable - [x] Document Method (and subclass) attributes (use Sphinx "#:" comments) - [x] Re-add Man pages (and auto-build them in CI) diff --git a/docs/usage.rst b/docs/usage.rst index 11f933d..1981721 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -4,40 +4,140 @@ Usage getmac provides a Python function, :func:`~getmac.getmac.get_mac_address`, and a command-line interface, :ref:`getmac `. The command-line interface is documented in :doc:`cli`. This page documents usage from Python as a library. +If a MAC address was found, :func:`~getmac.getmac.get_mac_address` will return a lowercase colon-separated MAC address as a string. If no MAC was found, or an exception occurred, :obj:`None` is returned. + Examples ======== +Basic example: + +.. code-block:: shell + + Python 3.11.4 (tags/v3.11.4:d2340ef, Jun 7 2023, 05:45:37) [MSC v.1934 64 bit (AMD64)] on win32 + Type "help", "copyright", "credits" or "license" for more information. + Ctrl click to launch VS Code Native REPL + >>> from getmac import get_mac_address + >>> get_mac_address(ip='192.168.0.1') + '00:11:22:33:44:55' + + +Interface names +--------------- + +.. code-block:: python + + from getmac import get_mac_address + + # Linux example + get_mac_address(interface="eth0") + + # Windows example + get_mac_address(interface="Ethernet 3") + + +IP addresses and hostnames +-------------------------- + +.. code-block:: python + + from getmac import get_mac_address + + # IPv4 example + get_mac_address(ip="192.168.0.1") + + # IPv6 example + get_mac_address(ip6="::1") + + # Hostname example + get_mac_address(hostname="localhost") + + +Updated ARP/NDP table before lookup +----------------------------------- + +Update the ARP table (IPv4) or NDP list (IPv6) before looking up the MAC. +This results in a UDP packet being sent to the target IP address on port 55555 (by default). + + .. code-block:: python from getmac import get_mac_address - eth_mac = get_mac_address(interface="eth0") - win_mac = get_mac_address(interface="Ethernet 3") - ip_mac = get_mac_address(ip="192.168.0.1") - ip6_mac = get_mac_address(ip6="::1") - host_mac = get_mac_address(hostname="localhost") - updated_mac = get_mac_address(ip="10.0.0.1", network_request=True) + get_mac_address(ip="10.0.0.1", network_request=True) + + +Changing settings +----------------- + +Available settings are documented in :ref:`Configuration `. Here's an example of changing some settings at runtime: + +.. code-block:: python + + from getmac import get_mac_address, settings # Enable debugging - from getmac import getmac - getmac.DEBUG = 2 # DEBUG level 2 - print(getmac.get_mac_address(interface="Ethernet 3")) + settings.DEBUG = 2 # DEBUG level 2 + print(get_mac_address(interface="Ethernet 3")) # Change the UDP port used for updating the ARP table (UDP packet) - from getmac import getmac - getmac.PORT = 55555 # Default port is 55555 - print(getmac.get_mac_address(ip="192.168.0.1", network_request=True)) + settings.PORT = 55555 # Default port for getmac is 55555 + print(get_mac_address(ip="192.168.0.1", network_request=True)) + + +Using ipaddress objects +----------------------- + +You can also use the standard library's :mod:`ipaddress` module to specify IP addresses. This works for both IPv4 and IPv6 addresses. The ``ip`` argument also accepts ``IPv6Address`` and ``IPv6Interface`` objects, and will treat them as IPv6 addresses. The ``ip6`` argument is not needed when using these objects, but can be used if desired. + +.. code-block:: python + + from getmac import get_mac_address + from ipaddress import ip_address, IPv4Interface, IPv6Interface + + ipv4_addr = ip_address("192.168.0.1") + print(get_mac_address(ip=ipv4_addr)) + + ipv6_addr = ip_address("::1") + print(get_mac_address(ip=ipv6_addr)) + + ipv4_interface = IPv4Interface("192.168.0.1/24") + print(get_mac_address(ip=ipv4_interface)) + + ipv6_interface = IPv6Interface("::1/128") + print(get_mac_address(ip=ipv6_interface)) Configuration ============= -TODO: update these for new settings classes -!! - +Settings that affect the behavior of getmac are in the :mod:`~getmac.variables` module, with the exception of the logging settings, which are handled by Python's :mod:`logging` module. The following settings are available: -- ``logging.getLogger("getmac")``: Runtime messages and errors are recorded to the ``getmac`` logger using Python's :mod:`logging` module. They can be configured by using :func:`logging.basicConfig` or adding :class:`logging.Handler` instances to the logger named ``"getmac"``. +- ``logging.getLogger("getmac")``: Runtime messages and errors are recorded to the ``getmac`` logger using Python's :mod:`logging` module. They can be configured by using :func:`logging.basicConfig` or adding :class:`logging.Handler` instances to the ``getmac`` logger. - :attr:`~getmac.variables.Settings.DEBUG`: integer value that controls debugging output. The higher the value, the more output you get. - :attr:`~getmac.variables.Settings.PORT`: the UDP port used to populate the ARP table (IPv4) or NDP list (IPv6) when looking up MACs for hosts or IPs (see the documentation of the ``network_request`` argument in :func:`~getmac.getmac.get_mac_address` for details). - :attr:`getmac.variables.Settings.OVERRIDE_PLATFORM`: Override the platform detection with the given value (e.g. ``"linux"``, ``"windows"``, ``"freebsd"``, etc). Any values returned by :func:`platform.system` are valid. - :attr:`~getmac.variables.Settings.FORCE_METHOD`: Name of method to use. This will force a specific method to be used, e.g. :class:`getmac.getmac.IpNeighborShow` with the string ``"IpNeighborShow"``. This will be used regardless of the method's type or platform compatibility, and :func:`Method.test() ` will NOT be checked! The list of available methods is in :data:`getmac.getmac.METHODS`. + + +Notes +===== + +- ``"localhost"`` or ``"127.0.0.1"`` will always return ``"00:00:00:00:00:00"``, as this is the MAC address of the loopback interface. +- If none of the arguments are selected, the default network interface for the system will be used. If the default network interface cannot be determined, then it will attempt to fallback to typical defaults for the platform (`Ethernet` on Windows, `em0` on BSD, `en0` on OSX/Darwin, and `eth0` otherwise). If that fails, then it will fallback to `lo` on POSIX systems. +- The first four arguments are mutually exclusive. `network_request` does not have any functionality when the `interface` argument is specified, and can be safely set if using in a script. +- **Exceptions will be handled silently and returned as a None.** If you run into problems, you can set `DEBUG` to true and get more information about what's happening. If you're still having issues, please create an [issue on GitHub](https://github.com/GhostofGoes/getmac/issues) and include the output with `DEBUG` enabled. + + +Limitations & Known Issues +========================== + +- The physical transport is assumed to be Ethernet (802.3). Others, such as Wi-Fi (802.11), are currently not tested or considered. I plan to address this in the future, and am definitely open to pull requests or issues related to this, including error reports. +- "Remote hosts" refer to hosts in *your local layer 2 network*, also known as a "broadcast domain", "LAN", or "VLAN". As far as I know, there is not a reliable method to get a MAC address for a remote host external to the LAN. If you know any methods otherwise, please [open a GitHub issue](https://github.com/GhostofGoes/getmac/issues) or shoot me an email, I'd love to be wrong about this. +- Some methods may not work on certain platforms or configurations. If a method fails, getmac will try the next available method until it finds one that works or exhausts all options. +- The accuracy and reliability of the MAC address retrieval may vary depending on the network configuration, the target host's settings, and other factors. Please refer to the documentation of each method for more details on their behavior and limitations. +- Depending on the platform, there could be a performance detriment, due to heavy usage of regular expressions. +- Platform test coverage is imperfect. If you're having issues, then you might be using a platform I haven't been able to test. Keep calm, open a GitHub issue, and I'd be more than happy to help. +- Linux, WSL: Getting the mac of a local interface IP does not currently work (``getmac --ip 10.0.0.4`` will fail if ``10.0.0.4`` is the IP address of a local interface). This issue may be present on other POSIX systems as well. +- Hostnames for IPv6 devices are not yet supported. +- Windows: the "default" (used when no arguments set or specified) of selecting the default route interface only works effectively if ``network_request`` is enabled. If not, ``Ethernet`` is used as the default. +- IPv6 support is good but lags behind IPv4 in some places and isn't as well-tested across the supported platform set. From cac07c80070e9f8718d607b01212a6878ceb7701 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 20:21:00 -0700 Subject: [PATCH 097/112] feat: add getmac.get_default_interface() --- README.md | 7 ++++++- docs/misc_docs/TODO.md | 30 ++++++++++++++++++------------ docs/usage.rst | 28 ++++++++++++++++++++++------ getmac/getmac.py | 20 ++++++++++++++++++++ tests/test_getmac.py | 13 +++++++++++++ 5 files changed, 79 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 9ac8059..086130b 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ It provides a platform-independent interface to get the MAC addresses of: - System network interfaces (by interface name) - Remote hosts on the local network (by IPv4/IPv6 address or hostname) -It provides one function: `get_mac_address()` +It provides one primary function: `get_mac_address()` [![asciicast](https://asciinema.org/a/n3insrxfyECch6wxtJEl3LHfv.png)](https://asciinema.org/a/n3insrxfyECch6wxtJEl3LHfv) @@ -76,6 +76,11 @@ print(getmac.get_mac_address(interface="Ethernet 3")) # Change the UDP port used for updating the ARP table (UDP packet) settings.PORT = 44444 # Default is 55555 print(getmac.get_mac_address(ip="192.168.0.1", network_request=True)) + +# Get the name of the system's default network interface +# NOTE: this doesn't currently work on Windows +from getmac import get_default_interface +print(get_default_interface()) ``` ## Terminal examples diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index 18f5a51..ee8c8a1 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -2,7 +2,6 @@ # 1.0.0 release ## Documentation -- [ ] Add guide on using the modules API, e.g. registering a new method in `getmac.getmac.METHODS`, etc. - [x] Single page on RTD/publish with GitHub actions built with Sphinx and Furo - [x] Update docs/usage examples for `get_mac_address()` - [x] Document possible values for `PLATFORM` variable @@ -24,7 +23,7 @@ ## Features - [x] Support `ipaddress` objects, `IPv4Address` and `IPv6Address` -- [ ] Add new method: `get_default_interface()`. This leverages the default interface detection methods to expose a helpful public API. +- [x] Add new method: `get_default_interface()`. This leverages the default interface detection methods to expose a helpful public API. - [ ] [issue 77](https://github.com/GhostofGoes/getmac/issues/77): Feature: get all mac addresses - "As I was thinking of adding support to jaraco.net for supporting macOS devices (IP addresses and mac addresses), I thought getmac might be a helpful solution, but as I delved into it, I could see that getmac only returns a single mac, even though there may be multiple on a host. It would be nice if getmac could abstract some of its behaviors, mainly to allow a user to query for all mac addresses represented by the host." @@ -47,9 +46,6 @@ ## Enhancements/fixes/misc. - [x] Python 3.13 + 3.14 -- [ ] Properly support WSL2 -- [ ] address all TODOs in the code -- [ ] implement proper default interface detection on Windows - [ ] [issue #76](https://github.com/GhostofGoes/getmac/issues/76): get_mac_address() is caching an old mac address, no longer present in local ARP - get_mac_address() is caching an old mac address for a given IP, even when it has timeout from OS ARP table. Only an explicit delete of the ARP entry on the OS make it return '00:00:00:00:00:00' again. - Fix is to check that the flag != 0x0, which should do the trick, unless there's an edge case that it misses. @@ -92,13 +88,10 @@ Removing Tox, maybe. Can do version enumeration in GitHub Actions, for all platf # Etc -- [ ] Add to Conda Forge ([example here](https://github.com/conda-forge/staged-recipes/pull/26828/files)) -- [x] Add [isort](https://pycqa.github.io/isort/) (requires python 3.8+) - [ ] cache the result of executable checks in `getmac.utils.popen()` - [ ] Refactor the default interface code. Combine the functions into one, move the default fallback logic into the function. - TODO: MAC -> IP. "to_find='mac'"? (create GitHub issue?) -- [ ] Move method classes into a separate file # Bugs or potential issues @@ -155,10 +148,26 @@ This is going to be a bit more complicated since the highest metric routes are g - [ ] Add ability to get the mac address of a Python socket's interface (`socket.socket`) - [ ] Test against non-ethernet interfaces (WiFi, LTE, etc.) - [ ] Create a script to collect samples for all relevant commands on a platform and save output into the appropriately named sub-directory in `samples/`. -- [ ] Use [ruff](https://github.com/charliermarsh/ruff) instead of `flake8`? FastAPI, Scikit learn, [PyPA build](https://github.com/pypa/build/pull/565/), and other major projects are moving to it +- [x] Use [ruff](https://github.com/charliermarsh/ruff) instead of `flake8`? FastAPI, Scikit learn, [PyPA build](https://github.com/pypa/build/pull/565/), and other major projects are moving to it + + +# Documentation +- [ ] Add guide on using the modules API, e.g. registering a new method in `getmac.getmac.METHODS`, etc. +- [ ] Write a short guide on how to add and test a new method + + +# Dev +- [ ] Automatically publish to PyPI when publishing a release on GitHub +- [ ] Add typing stubs to [typeshed](https://github.com/python/typeshed) once getmac 1.0.0 is released ([guide](https://github.com/python/typeshed/blob/master/CONTRIBUTING.md)) +- [ ] Add to Conda Forge ([example here](https://github.com/conda-forge/staged-recipes/pull/26828/files)) +- [x] Add [isort](https://pycqa.github.io/isort/) (requires python 3.8+) +- [ ] Move method classes into a separate file # Post-1.0.0 +- [ ] Properly support WSL2 +- [ ] address all TODOs in the code +- [ ] implement proper default interface detection on Windows - [ ] FreeBSD default interface: `route get default` - [ ] Support NetBSD - platform: `netbsd` @@ -177,11 +186,8 @@ This is going to be a bit more complicated since the highest metric routes are g - [ ] Refactor to build a local state of the interfaces on the system, and use that as fallback for default lookup of interface with no name. Could also include MACs for faster lookup of future interface queries. Similar to how `netifaces` works, with a dict with interface infos. Properly address https://github.com/GhostofGoes/getmac/issues/78 - [ ] New method for "ip addr"? (this would be useful for CentOS and others as a fallback) - [ ] Method-specific loggers? dynamically set logger name based on subclass name, so we don't have to manually set it in the string -- [ ] Add typing stubs to [typeshed](https://github.com/python/typeshed) once getmac 1.0.0 is released ([guide](https://github.com/python/typeshed/blob/master/CONTRIBUTING.md)) - [ ] Use `__import__()` or `importlib`? - [ ] Parameterize regexes? (is this any faster?) -- [ ] Write a short guide on how to add and test a new method -- [ ] Automatically publish to PyPI when publishing a release on GitHub - [ ] Add support for Unix and Windows interface indices as a separate argument to `get_mac_address`. On Windows, we could use `wmic`, while on Unix and Python 3 we can use `socket.if_indextoname()`. - [ ] API to add/remove methods at runtime (including new, custom methods) - [ ] Reduce duplication, for example "if not arg: return None" diff --git a/docs/usage.rst b/docs/usage.rst index 1981721..25d3d93 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -69,7 +69,7 @@ This results in a UDP packet being sent to the target IP address on port 55555 ( Changing settings ----------------- -Available settings are documented in :ref:`Configuration `. Here's an example of changing some settings at runtime: +Available settings are documented in :ref:`configuration`. Here's an example of changing some settings at runtime: .. code-block:: python @@ -100,12 +100,28 @@ You can also use the standard library's :mod:`ipaddress` module to specify IP ad ipv6_addr = ip_address("::1") print(get_mac_address(ip=ipv6_addr)) - ipv4_interface = IPv4Interface("192.168.0.1/24") - print(get_mac_address(ip=ipv4_interface)) + ipv4_iface = IPv4Interface("192.168.0.1/24") + print(get_mac_address(ip=ipv4_iface)) - ipv6_interface = IPv6Interface("::1/128") - print(get_mac_address(ip=ipv6_interface)) + ipv6_iface = IPv6Interface("::1/128") + print(get_mac_address(ip=ipv6_iface)) +Default interface name +---------------------- + +Get the name of the system's default network interface using :func:`~getmac.getmac.get_default_interface`. This leverages the same logic that getmac uses internally to determine the default interface (e.g. for when ``get_mac_address()`` is called without any arguments). + +.. warning:: + This currently doesn't work on Windows platforms. + It should work on other platforms, including Linux, OSX, and most BSDs. + +.. code-block:: python + + from getmac import get_default_interface + print(get_default_interface()) + + +.. _configuration: Configuration ============= @@ -115,7 +131,7 @@ Settings that affect the behavior of getmac are in the :mod:`~getmac.variables` - ``logging.getLogger("getmac")``: Runtime messages and errors are recorded to the ``getmac`` logger using Python's :mod:`logging` module. They can be configured by using :func:`logging.basicConfig` or adding :class:`logging.Handler` instances to the ``getmac`` logger. - :attr:`~getmac.variables.Settings.DEBUG`: integer value that controls debugging output. The higher the value, the more output you get. - :attr:`~getmac.variables.Settings.PORT`: the UDP port used to populate the ARP table (IPv4) or NDP list (IPv6) when looking up MACs for hosts or IPs (see the documentation of the ``network_request`` argument in :func:`~getmac.getmac.get_mac_address` for details). -- :attr:`getmac.variables.Settings.OVERRIDE_PLATFORM`: Override the platform detection with the given value (e.g. ``"linux"``, ``"windows"``, ``"freebsd"``, etc). Any values returned by :func:`platform.system` are valid. +- :attr:`~getmac.variables.Settings.OVERRIDE_PLATFORM`: Override the platform detection with the given value (e.g. ``"linux"``, ``"windows"``, ``"freebsd"``, etc). Any values returned by :func:`platform.system` are valid. - :attr:`~getmac.variables.Settings.FORCE_METHOD`: Name of method to use. This will force a specific method to be used, e.g. :class:`getmac.getmac.IpNeighborShow` with the string ``"IpNeighborShow"``. This will be used regardless of the method's type or platform compatibility, and :func:`Method.test() ` will NOT be checked! The list of available methods is in :data:`getmac.getmac.METHODS`. diff --git a/getmac/getmac.py b/getmac/getmac.py index a35e571..e81bfc3 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -1801,3 +1801,23 @@ def get_mac_address( gvars.log.debug(f"getmac took {duration:0.4f} seconds") return utils.clean_mac(mac) + + +def get_default_interface() -> Optional[str]: + """ + Get the name of the default network interface on the system. + + This is essentially a convenience wrapper around + :func:`get_by_method` with the ``default_iface`` method type. + The code is literally + ``return getmac.getmac.get_by_method("default_iface")``. + + .. note:: + This currently doesn't work on Windows platforms. + It should work on other platforms, including Linux, OSX, and most BSDs. + + Returns: + The name of the default network interface, or :obj:`None` + if it could not be found or an exception occurred. + """ + return get_by_method("default_iface") diff --git a/tests/test_getmac.py b/tests/test_getmac.py index 94173a6..94abc42 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -347,3 +347,16 @@ def test_get_mac_address_invalid_types(): with pytest.raises(ValueError, match="Unknown type for 'ip6' argument"): getmac.get_mac_address(ip6=object()) + + +def test_get_default_interface(mocker, get_sample): + mocker.patch( + "getmac.getmac.METHOD_CACHE", + { + "default_iface": getmac.DefaultIfaceOpenBsd(), + }, + ) + + content = get_sample("openbsd_6/route_nq_show_inet_gateway_priority_1.out") + mocker.patch("getmac.utils.popen", return_value=content) + assert getmac.get_default_interface() == "em0" From 732503ad7c7ba9566198ef067d7f84c966f2647a Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 20:41:27 -0700 Subject: [PATCH 098/112] fix: remove UuidArpGetNode method --- CHANGELOG.md | 2 ++ docs/misc_docs/TODO.md | 2 +- getmac/getmac.py | 42 +----------------------------------------- tests/test_methods.py | 15 --------------- 4 files changed, 4 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f110d2c..6aa65b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ * Removed support for Jython. As of Jan 23rd, 2023, [Jython](https://github.com/jython/jython) still does not support Python3, only 2.7. If and when it supports Python 3, I'll re-add support for it. * Removed explicit support for IronPython. [IronPython3 exists](https://github.com/IronLanguages/ironpython3), however I don't have a way to test it in CI. If someone knows of a way to test it in GitHub actions, let me know, and I'm happy to re-add explicit support. * Removed RPM packaging. This wasn't being used anywhere, and hasn't been updated since version 0.6.0. Way back when, I dreamt of putting this in OS package repositories. However, I've now learned that's up to the maintainers of those repos (e.g., Ubuntu or Fedora maintainers), not me. +* Remove `UuidArpGetnode` method. It's quite slow (performs up to 3 subprocess calls internally) and the functionality is already implemented by `ArpVariousArgs`. + ### Dev * Renamed `master` branch to `main` diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index ee8c8a1..7c4d2bb 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -28,7 +28,7 @@ - "As I was thinking of adding support to jaraco.net for supporting macOS devices (IP addresses and mac addresses), I thought getmac might be a helpful solution, but as I delved into it, I could see that getmac only returns a single mac, even though there may be multiple on a host. It would be nice if getmac could abstract some of its behaviors, mainly to allow a user to query for all mac addresses represented by the host." ## Breaking changes (or potentially breaking) -- [ ] Replace the `UuidArpGetNode` method. It calls 3 commands and is quite inefficient, and doesn't exist in Python 3.9+. We should just take the methods and use directly. +- [x] Replace the `UuidArpGetNode` method. It calls 3 commands and is quite inefficient. It's functionality is already implemented by `ArpVariousArgs`. - [ ] Raise exceptions on critical failures (stuff that were warnings in 0.9.0), all calls to `_warn_critical()`. - [ ] **Consolidate `ip6` argument into `ip` argument.**. Parse based on `::` character vs `.` character if `str` or via `.version == 4`/`.version == 6` for `ipaddress` objects. - Combine `--ip` and `--ip6` CLI arguments into `--ip` output. this would make it *much* easier to test methods. diff --git a/getmac/getmac.py b/getmac/getmac.py index e81bfc3..99ad050 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -129,46 +129,6 @@ def __str__(cls) -> str: return cls.__name__ -# TODO(python3): do we want to keep this around? It calls 3 commands and is -# quite inefficient. We should just take the methods and use directly. -class UuidArpGetNode(Method): - """ - Uses Python's :func:`uuid._arp_getnode` function to get the - MAC address of a remote host using ARP. - """ - - platforms = {"linux", "darwin", "sunos", "other"} - method_type = "ip" - - def test(self) -> bool: - try: - from uuid import _arp_getnode # type: ignore # noqa: F401 - - return True - except Exception: - return False - - def get(self, arg: str) -> Optional[str]: - from uuid import _arp_getnode # type: ignore - - backup = socket.gethostbyname - try: - socket.gethostbyname = lambda x: arg # noqa: ARG005 - mac1 = _arp_getnode() - if mac1 is not None: - mac1 = utils.uuid_convert(mac1) - mac2 = _arp_getnode() - mac2 = utils.uuid_convert(mac2) - if mac1 == mac2: - return mac1 - except Exception: - raise - finally: - socket.gethostbyname = backup - - return None - - class ArpFile(Method): """ Use the contents of ``/proc/net/arp`` to find the MAC address of a host. @@ -252,6 +212,7 @@ class ArpVariousArgs(Method): _args = ( ("", True), # "arp 192.168.1.1" # Linux + # NOTE: "arp -an" was also used by uuid._arp_getnode() in CPython ("-an", False), # "arp -an" ("-an", True), # "arp -an 192.168.1.1" # Darwin, WSL, Linux distros??? @@ -1153,7 +1114,6 @@ def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 NetstatIface, IpNeighborShow, ArpVariousArgs, - UuidArpGetNode, DefaultIfaceLinuxRouteFile, DefaultIfaceIpRoute, DefaultIfaceRouteCommand, diff --git a/tests/test_methods.py b/tests/test_methods.py index cf984d4..bab09d0 100644 --- a/tests/test_methods.py +++ b/tests/test_methods.py @@ -1,7 +1,6 @@ import platform import socket import sys -import uuid from subprocess import CalledProcessError import pytest @@ -555,20 +554,6 @@ def test_fcntl_iface(mocker): m.assert_called_once_with(socket.AF_INET, socket.SOCK_DGRAM) -# Python 2.7.5 (CentOS 7) doesn't have this... -# The commit adding it: https://bit.ly/2Hnd7bN (no idea what release it was in) -@pytest.mark.skipif( - not hasattr(uuid, "_arp_getnode"), - reason="This version of Python doesn't have uuid._arp_getnode", -) -def test_uuid_arp_get_node(mocker): - mocker.patch("uuid._arp_getnode", return_value=278094213753144) - assert getmac.UuidArpGetNode().get("10.0.0.1") == "FC:EC:DA:D3:29:38" - mocker.patch("uuid._arp_getnode", return_value=None) - assert getmac.UuidArpGetNode().get("10.0.0.1") is None - assert getmac.UuidArpGetNode().get("en0") is None - - @pytest.mark.skipif( sys.version_info[0] == 3 and sys.version_info[1] >= 9, reason="Python 3.9+ doesn't have uuid._find_mac", From d14d8600257ad45b114af58302866693fcdf957d Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 20:52:55 -0700 Subject: [PATCH 099/112] doc: update changelog --- CHANGELOG.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aa65b2..dcdbc10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,9 @@ ## 1.0.0 (TBD) ### Added -* Full support for Python 3.10 and 3.11 -* Tentative support for Python 3.12 +* Rewrote documentation and published on GitHub Pages: https://ghostofgoes.github.io/getmac/index.html +* Support Python 3.10 - 3.14 +* New function, `getmac.get_default_interface()`, which returns the name of the system's default interface (NOTE: Windows is not supported currently). * Support for [ipaddress](https://docs.python.org/3/library/ipaddress.html) objects from the Python standard library: `IPv4Address`, `IPv4Interface`, `IPv6Address`, `IPv6Interface` - These can be used with the `ip` and `ip6` arguments to `get_mac_address()` - The `ip` argument will accept IPv6 objects and process them as if `ip6` was set. @@ -17,17 +18,15 @@ - `IPv4Network` and `IPv6Network` will result in a `ValueError`. They're networks, not hosts, and passing them makes no sense. * Handle `bytes` values for `interface`, `ip`, `ip6`, and `hostname` arguments to `get_mac_address()`. This will just call `decode("utf-8")` on the bytes. This is to ease usage with certain libraries and functions that return strings as bytes. - If you pass it some weird bytes that aren't decodable as utf-8, it's obviously going to break. Don't be dumb :) +* Added manpage for the command line interface (`getmac.1`) ### Changed * BREAKING CHANGE: refactored how settings are handled. Instead of module-level globals, they're implemented in a Settings singleton in `getmac.settings`. For example, `getmac.getmac.PORT` should now be `getmac.settings.PORT`. * Reduce size of wheel distribution (`.whl` file) ### Removed -* Removed support for Python 2.7. It's time. Supporting 2.7 has become an onerous burden on the project, and has prevented fully supporting 3.10 and 3.11 due to having to stay on an ancient version of pytest. I'm done supporting a version of the language that hasn't been updated in nearly 15 years, and has been wholly unsupported for over 5 years. -* Removed support for Python 3.4 and 3.5. These are quite old and there were a number of essential features added in 3.6+ that I'd like to use (f-strings, type annotations, mature `pathlib`, etc.) -* Removed support for Python 3.6. setuptools added `pyproject.toml` support in version 61.0.0, however only version 59 is supported for Python 3.6. This means a choice had to be made between moving to `pyproject.toml` or supporting Python 3.6. Given 3.6 has been EOL for over a year, I've decided to drop support. -* Removed support for Python 3.7, a lot of modern tooling no longer works with it as of late 2024. -* Removed support for Jython. As of Jan 23rd, 2023, [Jython](https://github.com/jython/jython) still does not support Python3, only 2.7. If and when it supports Python 3, I'll re-add support for it. +* Removed support for Python 2.7 - 3.7. Most of the tooling used by getmac no longer works with 3.7 and older. If you need to use one of these versions, pin to `getmac<1.0.0`. +* Removed support for Jython. As of Dec 2025, [Jython](https://github.com/jython/jython) still does not support Python 3. If and when it supports Python 3, I'll re-add support for it. * Removed explicit support for IronPython. [IronPython3 exists](https://github.com/IronLanguages/ironpython3), however I don't have a way to test it in CI. If someone knows of a way to test it in GitHub actions, let me know, and I'm happy to re-add explicit support. * Removed RPM packaging. This wasn't being used anywhere, and hasn't been updated since version 0.6.0. Way back when, I dreamt of putting this in OS package repositories. However, I've now learned that's up to the maintainers of those repos (e.g., Ubuntu or Fedora maintainers), not me. * Remove `UuidArpGetnode` method. It's quite slow (performs up to 3 subprocess calls internally) and the functionality is already implemented by `ArpVariousArgs`. From bf92515a74467fd168cf480d356913c15416efab Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 23:09:37 -0700 Subject: [PATCH 100/112] test: add samples from Factor project --- tests/samples/third_party/facter/LICENSE | 202 ++++++++++++++++++ tests/samples/third_party/facter/README.md | 1 + .../facter/hpux/hpux_1111_ifconfig_lan0 | 2 + .../facter/hpux/hpux_1111_ifconfig_lan1 | 2 + .../facter/hpux/hpux_1111_ifconfig_lo0 | 2 + .../third_party/facter/hpux/hpux_1111_lanscan | 5 + .../facter/hpux/hpux_1111_netstat_in | 4 + .../hpux/hpux_1131_asterisk_ifconfig_lan0 | 2 + .../hpux/hpux_1131_asterisk_ifconfig_lan1 | 2 + .../hpux/hpux_1131_asterisk_ifconfig_lo0 | 2 + .../facter/hpux/hpux_1131_asterisk_lanscan | 5 + .../facter/hpux/hpux_1131_asterisk_netstat_in | 4 + .../facter/hpux/hpux_1131_ifconfig_lan0 | 2 + .../facter/hpux/hpux_1131_ifconfig_lan1 | 2 + .../facter/hpux/hpux_1131_ifconfig_lo0 | 2 + .../third_party/facter/hpux/hpux_1131_lanscan | 4 + .../facter/hpux/hpux_1131_netstat_in | 4 + .../hpux/hpux_1131_nic_bonding_ifconfig_lan1 | 2 + .../hpux/hpux_1131_nic_bonding_ifconfig_lan4 | 2 + .../hpux_1131_nic_bonding_ifconfig_lan4_1 | 2 + .../hpux/hpux_1131_nic_bonding_ifconfig_lo0 | 2 + .../facter/hpux/hpux_1131_nic_bonding_lanscan | 9 + .../hpux/hpux_1131_nic_bonding_netstat_in | 6 + .../ifconfig/6.0-STABLE_FreeBSD_ifconfig | 12 ++ .../facter/ifconfig/Mac_OS_X_10.5.5_ifconfig | 26 +++ .../bsd_ifconfig_all_with_multiple_interfaces | 18 ++ .../third_party/facter/ifconfig/centos_5_5 | 17 ++ .../facter/ifconfig/centos_5_5_eth0 | 8 + .../third_party/facter/ifconfig/darwin_10_3_0 | 26 +++ .../facter/ifconfig/darwin_10_3_0_en0 | 6 + .../third_party/facter/ifconfig/darwin_10_6_4 | 28 +++ .../facter/ifconfig/darwin_10_6_4_en1 | 6 + .../facter/ifconfig/darwin_10_6_6_dualstack | 8 + .../ifconfig/darwin_10_6_6_dualstack_en1 | 7 + .../third_party/facter/ifconfig/darwin_9_8_0 | 26 +++ .../facter/ifconfig/darwin_9_8_0_en0 | 6 + ...rwin_ifconfig_all_with_multiple_interfaces | 23 ++ .../ifconfig/darwin_ifconfig_single_interface | 6 + .../facter/ifconfig/debian_kfreebsd_ifconfig | 40 ++++ .../third_party/facter/ifconfig/fedora_10 | 36 ++++ .../facter/ifconfig/fedora_10_eth0 | 9 + .../third_party/facter/ifconfig/fedora_13 | 18 ++ .../facter/ifconfig/fedora_13_eth0 | 9 + .../third_party/facter/ifconfig/fedora_8 | 38 ++++ .../third_party/facter/ifconfig/fedora_8_eth0 | 9 + .../third_party/facter/ifconfig/freebsd_6_0 | 12 ++ ...inux_ifconfig_all_with_multiple_interfaces | 19 ++ .../facter/ifconfig/linux_ifconfig_ib0 | 8 + .../facter/ifconfig/linux_ifconfig_no_addr | 19 ++ .../facter/ifconfig/linux_ifconfig_no_mac | 8 + .../facter/ifconfig/linux_ifconfig_venet | 24 +++ .../facter/ifconfig/open_solaris_10 | 12 ++ .../facter/ifconfig/open_solaris_b132 | 20 ++ ...aris_ifconfig_all_with_multiple_interfaces | 8 + .../solaris_ifconfig_single_interface | 3 + ...unos_ifconfig_all_with_multiple_interfaces | 10 + .../third_party/facter/ifconfig/ubuntu_7_04 | 38 ++++ .../facter/ifconfig/ubuntu_7_04_eth0 | 9 + tests/samples/third_party/facter/ifconfig_mac | 90 ++++++++ .../third_party/facter/ip_-6_route_show | 1 + tests/samples/third_party/facter/ip_link_show | 4 + .../third_party/facter/ip_link_show_all | 6 + .../samples/third_party/facter/ip_route_show | 3 + .../third_party/facter/ip_route_show_default | 2 + .../third_party/facter/linux_ip_show_addr | 20 ++ .../third_party/facter/netstat/centos_5_5 | 5 + .../third_party/facter/netstat/darwin_10_3_0 | 35 +++ .../third_party/facter/netstat/darwin_10_6_4 | 29 +++ .../facter/netstat/darwin_10_6_6_dualstack | 34 +++ .../third_party/facter/netstat/darwin_9_8_0 | 28 +++ .../third_party/facter/netstat/fedora_10 | 7 + .../facter/netstat/open_solaris_10 | 16 ++ .../facter/netstat/open_solaris_b132 | 17 ++ .../third_party/facter/netstat/ubuntu_7_04 | 7 + tests/samples/third_party/facter/netstat_in | 6 + tests/samples/third_party/facter/netstat_rn | 13 ++ .../samples/third_party/facter/netstat_rn_two | 6 + .../samples/third_party/facter/proc_net_route | 3 + .../third_party/facter/proc_net_route_empty | 1 + .../third_party/facter/route_n_get_default | 8 + .../third_party/facter/solaris_ifconfig | 3 + ...s_netsh_addresses_with_multiple_interfaces | 35 +++ .../facter/windows_netsh_all_interfaces | 12 ++ .../facter/windows_netsh_single_interface | 7 + .../facter/windows_netsh_single_interface6 | 18 ++ 85 files changed, 1258 insertions(+) create mode 100644 tests/samples/third_party/facter/LICENSE create mode 100644 tests/samples/third_party/facter/README.md create mode 100644 tests/samples/third_party/facter/hpux/hpux_1111_ifconfig_lan0 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1111_ifconfig_lan1 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1111_ifconfig_lo0 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1111_lanscan create mode 100644 tests/samples/third_party/facter/hpux/hpux_1111_netstat_in create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_asterisk_ifconfig_lan0 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_asterisk_ifconfig_lan1 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_asterisk_ifconfig_lo0 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_asterisk_lanscan create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_asterisk_netstat_in create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_ifconfig_lan0 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_ifconfig_lan1 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_ifconfig_lo0 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_lanscan create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_netstat_in create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lan1 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lan4 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lan4_1 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lo0 create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_lanscan create mode 100644 tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_netstat_in create mode 100644 tests/samples/third_party/facter/ifconfig/6.0-STABLE_FreeBSD_ifconfig create mode 100644 tests/samples/third_party/facter/ifconfig/Mac_OS_X_10.5.5_ifconfig create mode 100644 tests/samples/third_party/facter/ifconfig/bsd_ifconfig_all_with_multiple_interfaces create mode 100644 tests/samples/third_party/facter/ifconfig/centos_5_5 create mode 100644 tests/samples/third_party/facter/ifconfig/centos_5_5_eth0 create mode 100644 tests/samples/third_party/facter/ifconfig/darwin_10_3_0 create mode 100644 tests/samples/third_party/facter/ifconfig/darwin_10_3_0_en0 create mode 100644 tests/samples/third_party/facter/ifconfig/darwin_10_6_4 create mode 100644 tests/samples/third_party/facter/ifconfig/darwin_10_6_4_en1 create mode 100644 tests/samples/third_party/facter/ifconfig/darwin_10_6_6_dualstack create mode 100644 tests/samples/third_party/facter/ifconfig/darwin_10_6_6_dualstack_en1 create mode 100644 tests/samples/third_party/facter/ifconfig/darwin_9_8_0 create mode 100644 tests/samples/third_party/facter/ifconfig/darwin_9_8_0_en0 create mode 100644 tests/samples/third_party/facter/ifconfig/darwin_ifconfig_all_with_multiple_interfaces create mode 100644 tests/samples/third_party/facter/ifconfig/darwin_ifconfig_single_interface create mode 100644 tests/samples/third_party/facter/ifconfig/debian_kfreebsd_ifconfig create mode 100644 tests/samples/third_party/facter/ifconfig/fedora_10 create mode 100644 tests/samples/third_party/facter/ifconfig/fedora_10_eth0 create mode 100644 tests/samples/third_party/facter/ifconfig/fedora_13 create mode 100644 tests/samples/third_party/facter/ifconfig/fedora_13_eth0 create mode 100644 tests/samples/third_party/facter/ifconfig/fedora_8 create mode 100644 tests/samples/third_party/facter/ifconfig/fedora_8_eth0 create mode 100644 tests/samples/third_party/facter/ifconfig/freebsd_6_0 create mode 100644 tests/samples/third_party/facter/ifconfig/linux_ifconfig_all_with_multiple_interfaces create mode 100644 tests/samples/third_party/facter/ifconfig/linux_ifconfig_ib0 create mode 100644 tests/samples/third_party/facter/ifconfig/linux_ifconfig_no_addr create mode 100644 tests/samples/third_party/facter/ifconfig/linux_ifconfig_no_mac create mode 100644 tests/samples/third_party/facter/ifconfig/linux_ifconfig_venet create mode 100644 tests/samples/third_party/facter/ifconfig/open_solaris_10 create mode 100644 tests/samples/third_party/facter/ifconfig/open_solaris_b132 create mode 100644 tests/samples/third_party/facter/ifconfig/solaris_ifconfig_all_with_multiple_interfaces create mode 100644 tests/samples/third_party/facter/ifconfig/solaris_ifconfig_single_interface create mode 100644 tests/samples/third_party/facter/ifconfig/sunos_ifconfig_all_with_multiple_interfaces create mode 100644 tests/samples/third_party/facter/ifconfig/ubuntu_7_04 create mode 100644 tests/samples/third_party/facter/ifconfig/ubuntu_7_04_eth0 create mode 100644 tests/samples/third_party/facter/ifconfig_mac create mode 100644 tests/samples/third_party/facter/ip_-6_route_show create mode 100644 tests/samples/third_party/facter/ip_link_show create mode 100644 tests/samples/third_party/facter/ip_link_show_all create mode 100644 tests/samples/third_party/facter/ip_route_show create mode 100644 tests/samples/third_party/facter/ip_route_show_default create mode 100644 tests/samples/third_party/facter/linux_ip_show_addr create mode 100644 tests/samples/third_party/facter/netstat/centos_5_5 create mode 100644 tests/samples/third_party/facter/netstat/darwin_10_3_0 create mode 100644 tests/samples/third_party/facter/netstat/darwin_10_6_4 create mode 100644 tests/samples/third_party/facter/netstat/darwin_10_6_6_dualstack create mode 100644 tests/samples/third_party/facter/netstat/darwin_9_8_0 create mode 100644 tests/samples/third_party/facter/netstat/fedora_10 create mode 100644 tests/samples/third_party/facter/netstat/open_solaris_10 create mode 100644 tests/samples/third_party/facter/netstat/open_solaris_b132 create mode 100644 tests/samples/third_party/facter/netstat/ubuntu_7_04 create mode 100644 tests/samples/third_party/facter/netstat_in create mode 100644 tests/samples/third_party/facter/netstat_rn create mode 100644 tests/samples/third_party/facter/netstat_rn_two create mode 100644 tests/samples/third_party/facter/proc_net_route create mode 100644 tests/samples/third_party/facter/proc_net_route_empty create mode 100644 tests/samples/third_party/facter/route_n_get_default create mode 100644 tests/samples/third_party/facter/solaris_ifconfig create mode 100644 tests/samples/third_party/facter/windows_netsh_addresses_with_multiple_interfaces create mode 100644 tests/samples/third_party/facter/windows_netsh_all_interfaces create mode 100644 tests/samples/third_party/facter/windows_netsh_single_interface create mode 100644 tests/samples/third_party/facter/windows_netsh_single_interface6 diff --git a/tests/samples/third_party/facter/LICENSE b/tests/samples/third_party/facter/LICENSE new file mode 100644 index 0000000..427417b --- /dev/null +++ b/tests/samples/third_party/facter/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tests/samples/third_party/facter/README.md b/tests/samples/third_party/facter/README.md new file mode 100644 index 0000000..07ffd2c --- /dev/null +++ b/tests/samples/third_party/facter/README.md @@ -0,0 +1 @@ +The files in this folder are from the Facter project (https://github.com/puppetlabs/facter) and are licensed with the Apache 2.0 license. The files used are unmodified and only used as input data for unit tests. diff --git a/tests/samples/third_party/facter/hpux/hpux_1111_ifconfig_lan0 b/tests/samples/third_party/facter/hpux/hpux_1111_ifconfig_lan0 new file mode 100644 index 0000000..485814e --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1111_ifconfig_lan0 @@ -0,0 +1,2 @@ +lan0: flags=843 + inet 192.168.3.10 netmask ffffff00 broadcast 192.168.3.255 diff --git a/tests/samples/third_party/facter/hpux/hpux_1111_ifconfig_lan1 b/tests/samples/third_party/facter/hpux/hpux_1111_ifconfig_lan1 new file mode 100644 index 0000000..c3d9277 --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1111_ifconfig_lan1 @@ -0,0 +1,2 @@ +lan1: flags=843 + inet 10.1.1.6 netmask ffffff00 broadcast 10.1.1.255 diff --git a/tests/samples/third_party/facter/hpux/hpux_1111_ifconfig_lo0 b/tests/samples/third_party/facter/hpux/hpux_1111_ifconfig_lo0 new file mode 100644 index 0000000..5f09a3e --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1111_ifconfig_lo0 @@ -0,0 +1,2 @@ +lo0: flags=849 + inet 127.0.0.1 netmask ff000000 diff --git a/tests/samples/third_party/facter/hpux/hpux_1111_lanscan b/tests/samples/third_party/facter/hpux/hpux_1111_lanscan new file mode 100644 index 0000000..75ccd6c --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1111_lanscan @@ -0,0 +1,5 @@ +Hardware Station Crd Hdw Net-Interface NM MAC HP-DLPI DLPI +Path Address In# State NamePPA ID Type Support Mjr# +0/0/0/0 0x00307F0C79DC 0 UP lan0 snap0 1 ETHER Yes 119 +0/10/0/0/6/0 0x0010797B5CDE 1 UP lan1 snap1 2 ETHER Yes 119 +0/10/0/0/7/0 0x0010797B5CDF 2 UP lan2 snap2 3 ETHER Yes 119 diff --git a/tests/samples/third_party/facter/hpux/hpux_1111_netstat_in b/tests/samples/third_party/facter/hpux/hpux_1111_netstat_in new file mode 100644 index 0000000..1cfe0de --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1111_netstat_in @@ -0,0 +1,4 @@ +Name Mtu Network Address Ipkts Opkts +lan1 1500 10.1.1.0 10.1.1.6 435347580 1287271 +lan0 1500 192.168.3.0 192.168.3.10 28101904 3569941319 +lo0 4136 127.0.0.0 127.0.0.1 5071536 5071539 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_ifconfig_lan0 b/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_ifconfig_lan0 new file mode 100644 index 0000000..00088cf --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_ifconfig_lan0 @@ -0,0 +1,2 @@ +lan0: flags=843 + inet 192.168.3.9 netmask ffffff00 broadcast 192.168.3.255 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_ifconfig_lan1 b/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_ifconfig_lan1 new file mode 100644 index 0000000..df739cb --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_ifconfig_lan1 @@ -0,0 +1,2 @@ +lan1: flags=842 + inet 10.10.0.5 netmask ffffff00 broadcast 10.10.0.255 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_ifconfig_lo0 b/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_ifconfig_lo0 new file mode 100644 index 0000000..5f09a3e --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_ifconfig_lo0 @@ -0,0 +1,2 @@ +lo0: flags=849 + inet 127.0.0.1 netmask ff000000 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_lanscan b/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_lanscan new file mode 100644 index 0000000..a2dd634 --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_lanscan @@ -0,0 +1,5 @@ +Hardware Station Crd Hdw Net-Interface NM MAC HP-DLPI DLPI +Path Address In# State NamePPA ID Type Support Mjr# +0/0/0/0 0x00305D0626B2 0 UP lan0 snap0 1 ETHER Yes 119 +0/4/2/0/6/0 0x0010797BBE46 1 UP lan1 snap1 2 ETHER Yes 119 +0/4/2/0/7/0 0x0010797BBE47 2 DOWN lan2 snap2 3 ETHER Yes 119 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_netstat_in b/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_netstat_in new file mode 100644 index 0000000..c1c7da6 --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_asterisk_netstat_in @@ -0,0 +1,4 @@ +Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll +lan1* 1500 10.10.0.0 10.10.0.5 786 0 240 0 0 +lan0 1500 192.168.3.0 192.168.3.9 1823744990 0 23598735 0 0 +lo0 4136 127.0.0.0 127.0.0.1 7048047 0 7048047 0 0 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_ifconfig_lan0 b/tests/samples/third_party/facter/hpux/hpux_1131_ifconfig_lan0 new file mode 100644 index 0000000..f91acc4 --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_ifconfig_lan0 @@ -0,0 +1,2 @@ +lan0: flags=1843 + inet 192.168.30.152 netmask ffffff00 broadcast 192.168.30.255 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_ifconfig_lan1 b/tests/samples/third_party/facter/hpux/hpux_1131_ifconfig_lan1 new file mode 100644 index 0000000..07a6f5b --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_ifconfig_lan1 @@ -0,0 +1,2 @@ +lan1: flags=1843 + inet 10.1.54.36 netmask ffffff00 broadcast 10.1.54.255 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_ifconfig_lo0 b/tests/samples/third_party/facter/hpux/hpux_1131_ifconfig_lo0 new file mode 100644 index 0000000..5f09a3e --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_ifconfig_lo0 @@ -0,0 +1,2 @@ +lo0: flags=849 + inet 127.0.0.1 netmask ff000000 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_lanscan b/tests/samples/third_party/facter/hpux/hpux_1131_lanscan new file mode 100644 index 0000000..4c78ee1 --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_lanscan @@ -0,0 +1,4 @@ +Hardware Station Crd Hdw Net-Interface NM MAC HP-DLPI DLPI +Path Address In# State NamePPA ID Type Support Mjr# +0/1/2/0 0x0012317D6209 0 UP lan0 snap0 1 ETHER Yes 119 +0/6/1/0 0x0017FD2D2A57 1 UP lan1 snap1 2 ETHER Yes 119 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_netstat_in b/tests/samples/third_party/facter/hpux/hpux_1131_netstat_in new file mode 100644 index 0000000..6656d41 --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_netstat_in @@ -0,0 +1,4 @@ +Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll +lan1 1500 10.1.54.0 10.1.54.36 117489535 0 1681709 0 79 +lan0 1500 192.168.30.0 192.168.30.152 964843646 0 1668475345 0 0 +lo0 4136 127.0.0.0 127.0.0.1 4658855 0 4658855 0 0 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lan1 b/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lan1 new file mode 100644 index 0000000..188cf2a --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lan1 @@ -0,0 +1,2 @@ +lan1: flags=1843 + inet 192.168.30.32 netmask ffffff00 broadcast 192.168.30.255 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lan4 b/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lan4 new file mode 100644 index 0000000..868f2e4 --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lan4 @@ -0,0 +1,2 @@ +lan4: flags=1843 + inet 192.168.32.75 netmask ffffff00 broadcast 192.168.32.255 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lan4_1 b/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lan4_1 new file mode 100644 index 0000000..7a19bd4 --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lan4_1 @@ -0,0 +1,2 @@ +lan4:1: flags=1843 + inet 192.168.1.197 netmask ffffff00 broadcast 192.168.1.255 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lo0 b/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lo0 new file mode 100644 index 0000000..5f09a3e --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_ifconfig_lo0 @@ -0,0 +1,2 @@ +lo0: flags=849 + inet 127.0.0.1 netmask ff000000 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_lanscan b/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_lanscan new file mode 100644 index 0000000..0365d63 --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_lanscan @@ -0,0 +1,9 @@ +Hardware Station Crd Hdw Net-Interface NM MAC HP-DLPI DLPI +Path Address In# State NamePPA ID Type Support Mjr# +0/1/2/0 0x001319BD1C2D 0 UP lan0 snap0 1 ETHER Yes 119 +0/2/1/0 0x0012819E48DE 1 UP lan1 snap1 2 ETHER Yes 119 +0/2/1/1 0x0012819E48DF 2 UP lan2 snap2 3 ETHER Yes 119 +0/4/2/0/6/0 0x001165EB7385 5 UP lan5 snap5 4 ETHER Yes 119 +0/5/2/0/6/0 0x001165EB73E6 3 UP lan3 snap3 5 ETHER Yes 119 +0/6/1/0 0x0012819E4A7E 4 UP lan4 snap4 6 ETHER Yes 119 +0/6/1/1 0x0012819E4A7F 6 UP lan6 snap6 7 ETHER Yes 119 diff --git a/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_netstat_in b/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_netstat_in new file mode 100644 index 0000000..ca5f915 --- /dev/null +++ b/tests/samples/third_party/facter/hpux/hpux_1131_nic_bonding_netstat_in @@ -0,0 +1,6 @@ +Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll +lan4:1 1500 192.168.1.0 192.168.1.197 121 0 6 0 0 +lan3* 1500 none none 0 0 0 0 0 +lan1 1500 192.168.30.0 192.168.30.32 211188606 0 132070934 0 0 +lo0 4136 127.0.0.0 127.0.0.1 513508160 0 513509185 0 0 +lan4 1500 192.168.32.0 192.168.32.75 2640827721 0 2257447701 0 0 diff --git a/tests/samples/third_party/facter/ifconfig/6.0-STABLE_FreeBSD_ifconfig b/tests/samples/third_party/facter/ifconfig/6.0-STABLE_FreeBSD_ifconfig new file mode 100644 index 0000000..3335708 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/6.0-STABLE_FreeBSD_ifconfig @@ -0,0 +1,12 @@ +fxp0: flags=8843 mtu 1500 + options=b + inet x.x.58.26 netmask 0xfffffff8 broadcast x.x.58.31 + inet x.x.58.27 netmask 0xffffffff broadcast x.x.58.27 + inet x.x.58.28 netmask 0xffffffff broadcast x.x.58.28 + inet x.x.58.29 netmask 0xffffffff broadcast x.x.58.29 + inet x.x.58.30 netmask 0xffffffff broadcast x.x.58.30 + ether 00:0e:0c:68:67:7c + media: Ethernet autoselect (100baseTX ) + status: active +lo0: flags=8049 mtu 16384 + inet 127.0.0.1 netmask 0xff000000 diff --git a/tests/samples/third_party/facter/ifconfig/Mac_OS_X_10.5.5_ifconfig b/tests/samples/third_party/facter/ifconfig/Mac_OS_X_10.5.5_ifconfig new file mode 100644 index 0000000..33f6609 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/Mac_OS_X_10.5.5_ifconfig @@ -0,0 +1,26 @@ +lo0: flags=8049 mtu 16384 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 + inet 127.0.0.1 netmask 0xff000000 + inet6 ::1 prefixlen 128 +gif0: flags=8010 mtu 1280 +stf0: flags=0<> mtu 1280 +en0: flags=8863 mtu 1500 + ether 00:1b:63:ae:02:66 + media: autoselect status: inactive + supported media: autoselect 10baseT/UTP 10baseT/UTP 10baseT/UTP 10baseT/UTP 100baseTX 100baseTX 100baseTX 100baseTX 1000baseT 1000baseT 1000baseT none +fw0: flags=8863 mtu 4078 + lladdr 00:1e:52:ff:fe:31:1a:80 + media: autoselect status: inactive + supported media: autoselect +en1: flags=8863 mtu 1500 + inet6 fe80::21e:52ff:fe70:d7b6%en1 prefixlen 64 scopeid 0x6 + inet 192.168.0.4 netmask 0xffffff00 broadcast 192.168.0.255 + ether 00:1e:52:70:d7:b6 + media: autoselect status: active + supported media: autoselect +vmnet8: flags=8863 mtu 1500 + inet 172.16.15.1 netmask 0xffffff00 broadcast 172.16.15.255 + ether 00:50:56:c0:00:08 +vmnet1: flags=8863 mtu 1500 + inet 192.168.89.1 netmask 0xffffff00 broadcast 192.168.89.255 + ether 00:50:56:c0:00:01 diff --git a/tests/samples/third_party/facter/ifconfig/bsd_ifconfig_all_with_multiple_interfaces b/tests/samples/third_party/facter/ifconfig/bsd_ifconfig_all_with_multiple_interfaces new file mode 100644 index 0000000..d5bff49 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/bsd_ifconfig_all_with_multiple_interfaces @@ -0,0 +1,18 @@ +bge0: flags=8843 metric 0 mtu 1500 + options=9b + ether 00:0b:db:93:09:67 + inet 131.252.208.203 netmask 0xffffff00 broadcast 131.252.208.255 + inet6 fe80::20b:dbff:fe93:967%bge0 prefixlen 64 scopeid 0x1 + inet6 2610:10:20:208:20b:dbff:fe93:967 prefixlen 64 autoconf + media: Ethernet autoselect (1000baseT ) + status: active +bge1: flags=8802 metric 0 mtu 1500 + options=9b + ether 00:0b:db:93:09:68 + media: Ethernet autoselect (none) + status: no carrier +lo0: flags=8049 metric 0 mtu 16384 + options=3 + inet 127.0.0.1 netmask 0xff000000 + inet6 ::1 prefixlen 128 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x3 diff --git a/tests/samples/third_party/facter/ifconfig/centos_5_5 b/tests/samples/third_party/facter/ifconfig/centos_5_5 new file mode 100644 index 0000000..673c780 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/centos_5_5 @@ -0,0 +1,17 @@ +eth0 Link encap:Ethernet HWaddr 16:8D:2A:15:17:91 + inet addr:100.100.100.92 Bcast:100.100.100.255 Mask:255.255.255.0 + inet6 addr: fe80::148d:2aff:fe15:1791/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:1914742 errors:0 dropped:0 overruns:0 frame:0 + TX packets:3933 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:178728699 (170.4 MiB) TX bytes:389936 (380.7 KiB) + +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope:Host + UP LOOPBACK RUNNING MTU:16436 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) diff --git a/tests/samples/third_party/facter/ifconfig/centos_5_5_eth0 b/tests/samples/third_party/facter/ifconfig/centos_5_5_eth0 new file mode 100644 index 0000000..d1ea3f1 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/centos_5_5_eth0 @@ -0,0 +1,8 @@ +eth0 Link encap:Ethernet HWaddr 16:8D:2A:15:17:91 + inet addr:100.100.100.92 Bcast:100.100.100.255 Mask:255.255.255.0 + inet6 addr: fe80::148d:2aff:fe15:1791/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:1914871 errors:0 dropped:0 overruns:0 frame:0 + TX packets:3960 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:178738891 (170.4 MiB) TX bytes:393862 (384.6 KiB) diff --git a/tests/samples/third_party/facter/ifconfig/darwin_10_3_0 b/tests/samples/third_party/facter/ifconfig/darwin_10_3_0 new file mode 100644 index 0000000..2759daa --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/darwin_10_3_0 @@ -0,0 +1,26 @@ +lo0: flags=8049 mtu 16384 + inet6 ::1 prefixlen 128 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 + inet 127.0.0.1 netmask 0xff000000 +gif0: flags=8010 mtu 1280 +stf0: flags=0<> mtu 1280 +en0: flags=8863 mtu 1500 + ether 00:17:f2:06:e3:c2 + inet6 fe80::217:f2ff:fe06:e3c2%en0 prefixlen 64 scopeid 0x4 + inet 100.100.104.12 netmask 0xffffff00 broadcast 100.100.104.255 + media: autoselect (1000baseT ) + status: active +en1: flags=8863 mtu 1500 + ether 00:17:f2:06:e3:c3 + media: autoselect + status: inactive +fw0: flags=8863 mtu 2030 + lladdr 00:16:cb:ff:fe:76:6e:be + media: autoselect + status: inactive +vmnet8: flags=8863 mtu 1500 + ether 00:50:56:c0:00:08 + inet 172.16.95.1 netmask 0xffffff00 broadcast 172.16.95.255 +vmnet1: flags=8863 mtu 1500 + ether 00:50:56:c0:00:01 + inet 172.16.201.1 netmask 0xffffff00 broadcast 172.16.201.255 diff --git a/tests/samples/third_party/facter/ifconfig/darwin_10_3_0_en0 b/tests/samples/third_party/facter/ifconfig/darwin_10_3_0_en0 new file mode 100644 index 0000000..f11391a --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/darwin_10_3_0_en0 @@ -0,0 +1,6 @@ +en0: flags=8863 mtu 1500 + ether 00:17:f2:06:e3:c2 + inet6 fe80::217:f2ff:fe06:e3c2%en0 prefixlen 64 scopeid 0x4 + inet 100.100.104.12 netmask 0xffffff00 broadcast 100.100.104.255 + media: autoselect (1000baseT ) + status: active diff --git a/tests/samples/third_party/facter/ifconfig/darwin_10_6_4 b/tests/samples/third_party/facter/ifconfig/darwin_10_6_4 new file mode 100644 index 0000000..9fa04b0 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/darwin_10_6_4 @@ -0,0 +1,28 @@ +lo0: flags=8049 mtu 16384 + inet6 ::1 prefixlen 128 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 + inet 127.0.0.1 netmask 0xff000000 +gif0: flags=8010 mtu 1280 +stf0: flags=0<> mtu 1280 +en0: flags=8863 mtu 1500 + ether 58:b0:35:fa:08:b1 + media: autoselect + status: inactive +en1: flags=8863 mtu 1500 + ether 58:b0:35:7f:25:b3 + inet6 fe80::5ab0:35ff:fe7f:25b3%en1 prefixlen 64 scopeid 0x5 + inet 192.168.100.230 netmask 0xffffff00 broadcast 192.168.100.255 + media: + status: active +fw0: flags=8863 mtu 4078 + lladdr c4:2c:03:ff:fe:fc:c8:aa + media: autoselect + status: inactive +vboxnet0: flags=8842 mtu 1500 + ether 0a:00:27:00:00:00 +vmnet1: flags=8863 mtu 1500 + ether 00:50:56:c0:00:01 + inet 172.16.135.1 netmask 0xffffff00 broadcast 172.16.135.255 +vmnet8: flags=8863 mtu 1500 + ether 00:50:56:c0:00:08 + inet 172.16.38.1 netmask 0xffffff00 broadcast 172.16.38.255 diff --git a/tests/samples/third_party/facter/ifconfig/darwin_10_6_4_en1 b/tests/samples/third_party/facter/ifconfig/darwin_10_6_4_en1 new file mode 100644 index 0000000..10e6c48 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/darwin_10_6_4_en1 @@ -0,0 +1,6 @@ +en1: flags=8863 mtu 1500 + ether 58:b0:35:7f:25:b3 + inet6 fe80::5ab0:35ff:fe7f:25b3%en1 prefixlen 64 scopeid 0x5 + inet 192.168.100.230 netmask 0xffffff00 broadcast 192.168.100.255 + media: + status: active diff --git a/tests/samples/third_party/facter/ifconfig/darwin_10_6_6_dualstack b/tests/samples/third_party/facter/ifconfig/darwin_10_6_6_dualstack new file mode 100644 index 0000000..819f56e --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/darwin_10_6_6_dualstack @@ -0,0 +1,8 @@ +lo0: flags=8049 mtu 16384 + inet 127.0.0.1 netmask 0xff000000 + inet6 ::1 prefixlen 128 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 +en0: flags=8863 mtu 1500 + ether 00:25:4b:ca:56:72 + media: autoselect + status: inactive diff --git a/tests/samples/third_party/facter/ifconfig/darwin_10_6_6_dualstack_en1 b/tests/samples/third_party/facter/ifconfig/darwin_10_6_6_dualstack_en1 new file mode 100644 index 0000000..eabb1f6 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/darwin_10_6_6_dualstack_en1 @@ -0,0 +1,7 @@ +en1: flags=8863 mtu 1500 + ether 00:25:00:48:19:ef + inet6 fe80::225:ff:fe48:19ef%en1 prefixlen 64 scopeid 0x5 + inet 192.168.1.207 netmask 0xffffff00 broadcast 192.168.1.255 + inet6 2000:44b4:32:400::1 prefixlen 64 + media: autoselect + status: active diff --git a/tests/samples/third_party/facter/ifconfig/darwin_9_8_0 b/tests/samples/third_party/facter/ifconfig/darwin_9_8_0 new file mode 100644 index 0000000..db7e623 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/darwin_9_8_0 @@ -0,0 +1,26 @@ +lo0: flags=8049 mtu 16384 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 + inet 127.0.0.1 netmask 0xff000000 + inet6 ::1 prefixlen 128 +gif0: flags=8010 mtu 1280 +stf0: flags=0<> mtu 1280 +en0: flags=8863 mtu 1500 + inet6 fe80::217:f2ff:fe06:e42e%en0 prefixlen 64 scopeid 0x4 + inet 100.100.107.4 netmask 0xffffff00 broadcast 100.100.107.255 + ether 00:17:f2:06:e4:2e + media: autoselect (1000baseT ) status: active + supported media: autoselect 10baseT/UTP 10baseT/UTP 10baseT/UTP 10baseT/UTP 100baseTX 100baseTX 100baseTX 100baseTX 1000baseT 1000baseT 1000baseT +en1: flags=8863 mtu 1500 + ether 00:17:f2:06:e4:2f + media: autoselect status: inactive + supported media: autoselect 10baseT/UTP 10baseT/UTP 10baseT/UTP 10baseT/UTP 100baseTX 100baseTX 100baseTX 100baseTX 1000baseT 1000baseT 1000baseT +fw0: flags=8863 mtu 2030 + lladdr 00:16:cb:ff:fe:76:66:f2 + media: autoselect status: inactive + supported media: autoselect +vmnet8: flags=8863 mtu 1500 + inet 192.168.210.1 netmask 0xffffff00 broadcast 192.168.210.255 + ether 00:50:56:c0:00:08 +vmnet1: flags=8863 mtu 1500 + inet 192.168.61.1 netmask 0xffffff00 broadcast 192.168.61.255 + ether 00:50:56:c0:00:01 diff --git a/tests/samples/third_party/facter/ifconfig/darwin_9_8_0_en0 b/tests/samples/third_party/facter/ifconfig/darwin_9_8_0_en0 new file mode 100644 index 0000000..1cbfb61 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/darwin_9_8_0_en0 @@ -0,0 +1,6 @@ +en0: flags=8863 mtu 1500 + inet6 fe80::217:f2ff:fe06:e42e%en0 prefixlen 64 scopeid 0x4 + inet 100.100.107.4 netmask 0xffffff00 broadcast 100.100.107.255 + ether 00:17:f2:06:e4:2e + media: autoselect (1000baseT ) status: active + supported media: autoselect 10baseT/UTP 10baseT/UTP 10baseT/UTP 10baseT/UTP 100baseTX 100baseTX 100baseTX 100baseTX 1000baseT 1000baseT 1000baseT diff --git a/tests/samples/third_party/facter/ifconfig/darwin_ifconfig_all_with_multiple_interfaces b/tests/samples/third_party/facter/ifconfig/darwin_ifconfig_all_with_multiple_interfaces new file mode 100644 index 0000000..a55999c --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/darwin_ifconfig_all_with_multiple_interfaces @@ -0,0 +1,23 @@ +lo0: flags=8049 mtu 16384 + inet6 ::1 prefixlen 128 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 + inet 127.0.0.1 netmask 0xff000000 +gif0: flags=8010 mtu 1280 +stf0: flags=0<> mtu 1280 +en0: flags=8863 mtu 1500 + ether 00:23:32:d5:ee:34 + inet6 fe80::223:32ff:fed5:ee34%en0 prefixlen 64 scopeid 0x4 + inet6 2610:10:20:209:223:32ff:fed5:ee34 prefixlen 64 autoconf + inet 131.252.209.140 netmask 0xffffff00 broadcast 131.252.209.255 + media: autoselect (100baseTX ) + status: active +en1: flags=8863 mtu 1500 + ether 00:11:33:22:55:44 + inet6 fe80::211:33ff:fe22:5544%en1 prefixlen 64 scopeid 0x5 + inet 131.252.246.129 netmask 0xfffffe00 broadcast 131.252.247.255 + media: autoselect + status: active +fw0: flags=8863 mtu 4078 + lladdr 00:23:32:ff:fe:d5:ee:34 + media: autoselect + status: inactive diff --git a/tests/samples/third_party/facter/ifconfig/darwin_ifconfig_single_interface b/tests/samples/third_party/facter/ifconfig/darwin_ifconfig_single_interface new file mode 100644 index 0000000..d1e8bd3 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/darwin_ifconfig_single_interface @@ -0,0 +1,6 @@ +en1: flags=8863 mtu 1500 + inet6 fe80::21c:b3ff:febe:81c9%en1 prefixlen 64 scopeid 0x6 + inet 10.0.0.101 netmask 0xffffff00 broadcast 10.0.0.255 + ether 00:1c:b3:be:81:c9 + media: autoselect status: active + supported media: autoselect diff --git a/tests/samples/third_party/facter/ifconfig/debian_kfreebsd_ifconfig b/tests/samples/third_party/facter/ifconfig/debian_kfreebsd_ifconfig new file mode 100644 index 0000000..44a3b62 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/debian_kfreebsd_ifconfig @@ -0,0 +1,40 @@ +em0: flags=8843 metric 0 mtu 1500 + options=209b + ether 0:11:a:59:67:90 + inet6 fe80::211:aff:fe59:6790%em0 prefixlen 64 scopeid 0x1 + nd6 options=3 + media: Ethernet autoselect (1000baseT ) + status: active +em1: flags=8843 metric 0 mtu 1500 + options=209b + ether 0:11:a:59:67:91 + inet6 fe80::211:aff:fe59:6791%em1 prefixlen 64 scopeid 0x2 + inet 192.168.10.10 netmask 0xffffff00 broadcast 192.168.10.255 + nd6 options=3 + media: Ethernet autoselect (100baseTX ) + status: active +bge0: flags=8802 metric 0 mtu 1500 + options=8009b + ether 0:14:c2:3f:ea:e4 + media: Ethernet autoselect (none) + status: no carrier +bge1: flags=8802 metric 0 mtu 1500 + options=8009b + ether 0:14:c2:3f:ea:e3 + media: Ethernet autoselect (none) + status: no carrier +lo0: flags=8049 metric 0 mtu 16384 + options=3 + inet6 ::1 prefixlen 128 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x5 + inet 127.0.0.1 netmask 0xff000000 + nd6 options=3 +vlan0: flags=8843 metric 0 mtu 1500 + options=3 + ether 0:11:a:59:67:90 + inet6 fe80::211:aff:fe59:6790%vlan0 prefixlen 64 scopeid 0x6 + inet 192.168.192.2 netmask 0xffffff00 broadcast 192.168.192.255 + nd6 options=3 + media: Ethernet autoselect (1000baseT ) + status: active + vlan: 192 parent interface: em0 diff --git a/tests/samples/third_party/facter/ifconfig/fedora_10 b/tests/samples/third_party/facter/ifconfig/fedora_10 new file mode 100644 index 0000000..c6c1535 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/fedora_10 @@ -0,0 +1,36 @@ +eth0 Link encap:Ethernet HWaddr 00:17:F2:06:E4:26 + inet addr:100.100.108.9 Bcast:100.100.108.255 Mask:255.255.255.0 + inet6 addr: fe80::217:f2ff:fe06:e426/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:47711100 errors:0 dropped:0 overruns:0 frame:0 + TX packets:45446436 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:17037006736 (15.8 GiB) TX bytes:9198820680 (8.5 GiB) + Memory:92c20000-92c40000 + +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope:Host + UP LOOPBACK RUNNING MTU:16436 Metric:1 + RX packets:366974 errors:0 dropped:0 overruns:0 frame:0 + TX packets:366974 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:33867944 (32.2 MiB) TX bytes:33867944 (32.2 MiB) + +vmnet1 Link encap:Ethernet HWaddr 00:50:56:C0:00:01 + inet addr:172.16.131.1 Bcast:172.16.131.255 Mask:255.255.255.0 + inet6 addr: fe80::250:56ff:fec0:1/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:20 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) + +vmnet8 Link encap:Ethernet HWaddr 00:50:56:C0:00:08 + inet addr:192.168.183.1 Bcast:192.168.183.255 Mask:255.255.255.0 + inet6 addr: fe80::250:56ff:fec0:8/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:20 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) diff --git a/tests/samples/third_party/facter/ifconfig/fedora_10_eth0 b/tests/samples/third_party/facter/ifconfig/fedora_10_eth0 new file mode 100644 index 0000000..2b046fa --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/fedora_10_eth0 @@ -0,0 +1,9 @@ +eth0 Link encap:Ethernet HWaddr 00:17:F2:06:E4:26 + inet addr:100.100.108.9 Bcast:100.100.108.255 Mask:255.255.255.0 + inet6 addr: fe80::217:f2ff:fe06:e426/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:47711864 errors:0 dropped:0 overruns:0 frame:0 + TX packets:45447195 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:17037147877 (15.8 GiB) TX bytes:9198873602 (8.5 GiB) + Memory:92c20000-92c40000 diff --git a/tests/samples/third_party/facter/ifconfig/fedora_13 b/tests/samples/third_party/facter/ifconfig/fedora_13 new file mode 100644 index 0000000..5e4266f --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/fedora_13 @@ -0,0 +1,18 @@ +eth0 Link encap:Ethernet HWaddr 00:17:F2:0D:9B:A8 + inet addr:100.100.108.27 Bcast:100.100.108.255 Mask:255.255.255.0 + inet6 addr: fe80::217:f2ff:fe0d:9ba8/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:1813272 errors:0 dropped:0 overruns:0 frame:0 + TX packets:1539207 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:1729725903 (1.6 GiB) TX bytes:1606599540 (1.4 GiB) + Memory:f2c20000-f2c40000 + +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope:Host + UP LOOPBACK RUNNING MTU:16436 Metric:1 + RX packets:482 errors:0 dropped:0 overruns:0 frame:0 + TX packets:482 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:48488 (47.3 KiB) TX bytes:48488 (47.3 KiB) diff --git a/tests/samples/third_party/facter/ifconfig/fedora_13_eth0 b/tests/samples/third_party/facter/ifconfig/fedora_13_eth0 new file mode 100644 index 0000000..1272d62 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/fedora_13_eth0 @@ -0,0 +1,9 @@ +eth0 Link encap:Ethernet HWaddr 00:17:F2:0D:9B:A8 + inet addr:100.100.108.27 Bcast:100.100.108.255 Mask:255.255.255.0 + inet6 addr: fe80::217:f2ff:fe0d:9ba8/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:1813275 errors:0 dropped:0 overruns:0 frame:0 + TX packets:1539208 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:1729726282 (1.6 GiB) TX bytes:1606599653 (1.4 GiB) + Memory:f2c20000-f2c40000 diff --git a/tests/samples/third_party/facter/ifconfig/fedora_8 b/tests/samples/third_party/facter/ifconfig/fedora_8 new file mode 100644 index 0000000..ffd55f8 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/fedora_8 @@ -0,0 +1,38 @@ +eth0 Link encap:Ethernet HWaddr 00:18:F3:F6:33:E5 + inet addr:64.71.191.242 Bcast:64.71.191.247 Mask:255.255.255.248 + inet6 addr: fe80::218:f3ff:fef6:33e5/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:834422948 errors:0 dropped:0 overruns:0 frame:0 + TX packets:1241890353 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:3777804409 (3.5 GiB) TX bytes:2061587234 (1.9 GiB) + Interrupt:21 + +eth0:0 Link encap:Ethernet HWaddr 00:18:F3:F6:33:E5 + inet addr:64.71.191.243 Bcast:64.71.191.247 Mask:255.255.255.248 + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + Interrupt:21 + +eth0:1 Link encap:Ethernet HWaddr 00:18:F3:F6:33:E5 + inet addr:64.71.191.244 Bcast:64.71.191.247 Mask:255.255.255.248 + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + Interrupt:21 + +eth0:2 Link encap:Ethernet HWaddr 00:18:F3:F6:33:E5 + inet addr:64.71.191.245 Bcast:64.71.191.247 Mask:255.255.255.248 + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + Interrupt:21 + +eth0:3 Link encap:Ethernet HWaddr 00:18:F3:F6:33:E5 + inet addr:64.71.191.246 Bcast:64.71.191.247 Mask:255.255.255.248 + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + Interrupt:21 + +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope:Host + UP LOOPBACK RUNNING MTU:16436 Metric:1 + RX packets:59385736 errors:0 dropped:0 overruns:0 frame:0 + TX packets:59385736 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:3519026710 (3.2 GiB) TX bytes:3519026710 (3.2 GiB) diff --git a/tests/samples/third_party/facter/ifconfig/fedora_8_eth0 b/tests/samples/third_party/facter/ifconfig/fedora_8_eth0 new file mode 100644 index 0000000..0c75dc8 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/fedora_8_eth0 @@ -0,0 +1,9 @@ +eth0 Link encap:Ethernet HWaddr 00:18:F3:F6:33:E5 + inet addr:64.71.191.242 Bcast:64.71.191.247 Mask:255.255.255.248 + inet6 addr: fe80::218:f3ff:fef6:33e5/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:834423032 errors:0 dropped:0 overruns:0 frame:0 + TX packets:1241890434 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:3777811119 (3.5 GiB) TX bytes:2061606027 (1.9 GiB) + Interrupt:21 diff --git a/tests/samples/third_party/facter/ifconfig/freebsd_6_0 b/tests/samples/third_party/facter/ifconfig/freebsd_6_0 new file mode 100644 index 0000000..3335708 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/freebsd_6_0 @@ -0,0 +1,12 @@ +fxp0: flags=8843 mtu 1500 + options=b + inet x.x.58.26 netmask 0xfffffff8 broadcast x.x.58.31 + inet x.x.58.27 netmask 0xffffffff broadcast x.x.58.27 + inet x.x.58.28 netmask 0xffffffff broadcast x.x.58.28 + inet x.x.58.29 netmask 0xffffffff broadcast x.x.58.29 + inet x.x.58.30 netmask 0xffffffff broadcast x.x.58.30 + ether 00:0e:0c:68:67:7c + media: Ethernet autoselect (100baseTX ) + status: active +lo0: flags=8049 mtu 16384 + inet 127.0.0.1 netmask 0xff000000 diff --git a/tests/samples/third_party/facter/ifconfig/linux_ifconfig_all_with_multiple_interfaces b/tests/samples/third_party/facter/ifconfig/linux_ifconfig_all_with_multiple_interfaces new file mode 100644 index 0000000..d944694 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/linux_ifconfig_all_with_multiple_interfaces @@ -0,0 +1,19 @@ +eth0 Link encap:Ethernet HWaddr 00:12:3f:be:22:01 + inet addr:131.252.209.153 Bcast:131.252.209.255 Mask:255.255.255.0 + inet6 addr: 2610:10:20:209:212:3fff:febe:2201/64 Scope:Global + inet6 addr: fe80::212:3fff:febe:2201/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:20793317 errors:0 dropped:0 overruns:0 frame:0 + TX packets:19583281 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:1723593481 (1.7 GB) TX bytes:283377282 (283.3 MB) + Interrupt:16 + +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope:Host + UP LOOPBACK RUNNING MTU:16436 Metric:1 + RX packets:31809 errors:0 dropped:0 overruns:0 frame:0 + TX packets:31809 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:2075836 (2.0 MB) TX bytes:2075836 (2.0 MB) diff --git a/tests/samples/third_party/facter/ifconfig/linux_ifconfig_ib0 b/tests/samples/third_party/facter/ifconfig/linux_ifconfig_ib0 new file mode 100644 index 0000000..27d87f7 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/linux_ifconfig_ib0 @@ -0,0 +1,8 @@ +ib0 Link encap:InfiniBand HWaddr 80:00:00:4A:FE:80:00:00:00:00:00:00:00:00:00:00:00:00:00:00 + inet addr:10.6.193.12 Bcast:10.6.193.255 Mask:255.255.255.0 + inet6 addr: fe80::202:c903:43:2721/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:65520 Metric:1 + RX packets:8 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1024 + RX bytes:448 (448.0 b) TX bytes:0 (0.0 b) diff --git a/tests/samples/third_party/facter/ifconfig/linux_ifconfig_no_addr b/tests/samples/third_party/facter/ifconfig/linux_ifconfig_no_addr new file mode 100644 index 0000000..dcfdaf1 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/linux_ifconfig_no_addr @@ -0,0 +1,19 @@ +em1: flags=4163 mtu 1500 + inet 131.252.209.153 netmask 255.255.255.0 broadcast 192.168.76.255 + inet6 2610:10:20:209:212:3fff:febe:2201 prefixlen 128 scopeid 0x0 + inet6 fe80::221:ccff:fe4b:297d prefixlen 64 scopeid 0x20 + ether 00:21:cc:4b:29:7d txqueuelen 1000 (Ethernet) + RX packets 27222144 bytes 31247414760 (29.1 GiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 10259038 bytes 7784519352 (7.2 GiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + device interrupt 20 memory 0xd2600000-d2620000 + +lo: flags=73 mtu 16436 + inet 127.0.0.1 netmask 255.0.0.0 + inet6 ::1 prefixlen 128 scopeid 0x10 + loop txqueuelen 0 (Local Loopback) + RX packets 257371 bytes 37104110 (35.3 MiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 257371 bytes 37104110 (35.3 MiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 diff --git a/tests/samples/third_party/facter/ifconfig/linux_ifconfig_no_mac b/tests/samples/third_party/facter/ifconfig/linux_ifconfig_no_mac new file mode 100644 index 0000000..13d6509 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/linux_ifconfig_no_mac @@ -0,0 +1,8 @@ +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + UP LOOPBACK RUNNING MTU:16436 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) + diff --git a/tests/samples/third_party/facter/ifconfig/linux_ifconfig_venet b/tests/samples/third_party/facter/ifconfig/linux_ifconfig_venet new file mode 100644 index 0000000..2b5cded --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/linux_ifconfig_venet @@ -0,0 +1,24 @@ +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope:Host + UP LOOPBACK RUNNING MTU:16436 Metric:1 + RX packets:334 errors:0 dropped:0 overruns:0 frame:0 + TX packets:334 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:16700 (16.7 KB) TX bytes:16700 (16.7 KB) + +venet0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 + inet addr:127.0.0.1 P-t-P:127.0.0.1 Bcast:0.0.0.0 Mask:255.255.255.255 + UP BROADCAST POINTOPOINT RUNNING NOARP MTU:1500 Metric:1 + RX packets:7622207 errors:0 dropped:0 overruns:0 frame:0 + TX packets:8183436 errors:0 dropped:1 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:2102750761 (2.1 GB) TX bytes:2795213667 (2.7 GB) + +venet0:0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 + inet addr:XXX.XXX.XXX.XX1 P-t-P:XXX.XXX.XXX.XX1 Bcast:0.0.0.0 Mask:255.255.255.255 + UP BROADCAST POINTOPOINT RUNNING NOARP MTU:1500 Metric:1 + +venet0:1 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 + inet addr:XXX.XXX.XXX.XX2 P-t-P:XXX.XXX.XXX.XX2 Bcast:0.0.0.0 Mask:255.255.255.255 + UP BROADCAST POINTOPOINT RUNNING NOARP MTU:1500 Metric:1 diff --git a/tests/samples/third_party/facter/ifconfig/open_solaris_10 b/tests/samples/third_party/facter/ifconfig/open_solaris_10 new file mode 100644 index 0000000..76536fd --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/open_solaris_10 @@ -0,0 +1,12 @@ +lo0: flags=2001000849 mtu 8232 index 1 + inet 127.0.0.1 netmask ff000000 +hme0: flags=1000843 mtu 1500 index 2 + inet 192.168.2.83 netmask ffffff00 broadcast 192.168.2.255 + ether 8:0:20:d1:6d:79 +lo0: flags=2002000849 mtu 8252 index 1 + inet6 ::1/128 +hme0: flags=2000841 mtu 1500 index 2 + inet6 fe80::a00:20ff:fed1:6d79/10 + ether 8:0:20:d1:6d:79 +hme0:1: flags=2080841 mtu 1500 index 2 + inet6 2404:130:0:1000:a00:20ff:fed1:6d79/64 diff --git a/tests/samples/third_party/facter/ifconfig/open_solaris_b132 b/tests/samples/third_party/facter/ifconfig/open_solaris_b132 new file mode 100644 index 0000000..9990a72 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/open_solaris_b132 @@ -0,0 +1,20 @@ +lo0: flags=2001000849 mtu 8232 index 1 + inet 127.0.0.1 netmask ff000000 +bge0: flags=1100943 mtu 1500 index 2 + inet 202.78.241.97 netmask fffffff8 broadcast 202.78.241.103 + ether 0:1e:c9:43:55:f9 +int0: flags=1100843 mtu 9000 index 3 + inet 172.31.255.254 netmask ffffff00 broadcast 172.31.255.255 + ether 2:8:20:89:75:75 +lo0: flags=2002000849 mtu 8252 index 1 + inet6 ::1/128 +bge0: flags=2100941 mtu 1500 index 2 + inet6 fe80::ff:0/10 + ether 0:1e:c9:43:55:f9 +bge0:1: flags=2180941 mtu 1500 index 2 + inet6 2404:130:40:10::ff:0/64 +int0: flags=2100841 mtu 9000 index 3 + inet6 fe80::ff:0/10 + ether 2:8:20:89:75:75 +int0:1: flags=2180841 mtu 9000 index 3 + inet6 2404:130:40:18::ff:0/64 diff --git a/tests/samples/third_party/facter/ifconfig/solaris_ifconfig_all_with_multiple_interfaces b/tests/samples/third_party/facter/ifconfig/solaris_ifconfig_all_with_multiple_interfaces new file mode 100644 index 0000000..f04ad5d --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/solaris_ifconfig_all_with_multiple_interfaces @@ -0,0 +1,8 @@ +lo0: flags=2001000849 mtu 8232 index 1 + inet 127.0.0.1 netmask ff000000 +e1000g0: flags=201004843 mtu 1500 index 2 + inet 192.168.162.130 netmask ffffff00 broadcast 192.168.162.255 +lo0: flags=2002000849 mtu 8252 index 1 + inet6 ::1/128 +e1000g0: flags=202004841 mtu 1500 index 2 + inet6 fe80::20c:29ff:fe09:627e/10 diff --git a/tests/samples/third_party/facter/ifconfig/solaris_ifconfig_single_interface b/tests/samples/third_party/facter/ifconfig/solaris_ifconfig_single_interface new file mode 100644 index 0000000..a408938 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/solaris_ifconfig_single_interface @@ -0,0 +1,3 @@ +e1000g0: flags=201004843 mtu 1500 index 2 + inet 172.16.15.138 netmask ffffff00 broadcast 172.16.15.255 + ether 0:c:29:c1:70:2a diff --git a/tests/samples/third_party/facter/ifconfig/sunos_ifconfig_all_with_multiple_interfaces b/tests/samples/third_party/facter/ifconfig/sunos_ifconfig_all_with_multiple_interfaces new file mode 100644 index 0000000..c30efe0 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/sunos_ifconfig_all_with_multiple_interfaces @@ -0,0 +1,10 @@ +lo0: flags=2001000849 mtu 8232 index 1 + inet 127.0.0.1 netmask ff000000 +bge0: flags=1000843 mtu 1500 index 2 + inet 131.252.209.59 netmask ffffff00 broadcast 131.252.209.255 +lo0: flags=2002000849 mtu 8252 index 1 + inet6 ::1/128 +bge0: flags=2000841 mtu 1500 index 2 + inet6 fe80::203:baff:fe27:a7c/10 +bge0:1: flags=2080841 mtu 1500 index 2 + inet6 2610:10:20:209:203:baff:fe27:a7c/64 diff --git a/tests/samples/third_party/facter/ifconfig/ubuntu_7_04 b/tests/samples/third_party/facter/ifconfig/ubuntu_7_04 new file mode 100644 index 0000000..d1b9ea3 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/ubuntu_7_04 @@ -0,0 +1,38 @@ +ath0 Link encap:Ethernet HWaddr 00:17:F2:49:E0:E6 + inet6 addr: fe80::217:f2ff:fe49:e0e6/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) + +ath0:avah Link encap:Ethernet HWaddr 00:17:F2:49:E0:E6 + inet addr:169.254.10.213 Bcast:169.254.255.255 Mask:255.255.0.0 + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + +eth0 Link encap:Ethernet HWaddr 00:16:CB:A6:D4:3A + inet addr:100.100.106.200 Bcast:100.100.106.255 Mask:255.255.255.0 + inet6 addr: fe80::216:cbff:fea6:d43a/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:1070 errors:0 dropped:0 overruns:0 frame:0 + TX packets:236 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:69905 (68.2 KiB) TX bytes:28435 (27.7 KiB) + Interrupt:17 + +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope:Host + UP LOOPBACK RUNNING MTU:16436 Metric:1 + RX packets:33 errors:0 dropped:0 overruns:0 frame:0 + TX packets:33 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:3067 (2.9 KiB) TX bytes:3067 (2.9 KiB) + +wifi0 Link encap:UNSPEC HWaddr 00-17-F2-49-E0-E6-00-00-00-00-00-00-00-00-00-00 + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:2655 errors:0 dropped:0 overruns:0 frame:24795 + TX packets:193 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:199 + RX bytes:754290 (736.6 KiB) TX bytes:8878 (8.6 KiB) + Interrupt:16 diff --git a/tests/samples/third_party/facter/ifconfig/ubuntu_7_04_eth0 b/tests/samples/third_party/facter/ifconfig/ubuntu_7_04_eth0 new file mode 100644 index 0000000..91eb31c --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig/ubuntu_7_04_eth0 @@ -0,0 +1,9 @@ +eth0 Link encap:Ethernet HWaddr 00:16:CB:A6:D4:3A + inet addr:100.100.106.200 Bcast:100.100.106.255 Mask:255.255.255.0 + inet6 addr: fe80::216:cbff:fea6:d43a/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:1131 errors:0 dropped:0 overruns:0 frame:0 + TX packets:280 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:73859 (72.1 KiB) TX bytes:35243 (34.4 KiB) + Interrupt:17 diff --git a/tests/samples/third_party/facter/ifconfig_mac b/tests/samples/third_party/facter/ifconfig_mac new file mode 100644 index 0000000..de3ad28 --- /dev/null +++ b/tests/samples/third_party/facter/ifconfig_mac @@ -0,0 +1,90 @@ +lo0: flags=8049 mtu 16384 + options=1203 + inet 127.0.0.1 netmask 0xff000000 + inet6 ::1 prefixlen 128 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 + nd6 options=201 +gif0: flags=8010 mtu 1280 +stf0: flags=0<> mtu 1280 +en0: flags=8863 mtu 1500 + options=400 + ether 64:5a:ed:ea:5c:81 + inet 192.168.143.212 netmask 0xffffff00 broadcast 192.168.143.255 + media: autoselect + status: active +en0.1: flags=8843 metric 0 mtu 1500 + options=3 + ether 08:00:27:f5:23:f7 + inet 0.0.0.0 netmask 0xff000000 broadcast 255.255.255.255 + groups: vlan + vlan: 1 vlanpcp: 0 parent interface: em0 + media: autoselect + status: active + nd6 options=29 +en1: flags=8963 mtu 1500 + options=460 + ether 82:17:0e:93:9d:00 + media: autoselect + status: inactive +en2: flags=8963 mtu 1500 + options=460 + ether 82:17:0e:93:9d:01 + media: autoselect + status: inactive +bridge0: flags=8863 mtu 1500 + options=63 + ether 82:17:0e:93:9d:00 + Configuration: + id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0 + maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200 + root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0 + ipfilter disabled flags 0x0 + member: en1 flags=3 + ifmaxaddr 0 port 5 priority 0 path cost 0 + member: en2 flags=3 + ifmaxaddr 0 port 6 priority 0 path cost 0 + media: + status: inactive +p2p0: flags=8843 mtu 2304 + options=400 + ether 06:5a:ed:ea:5c:81 + media: autoselect + status: inactive +awdl0: flags=8943 mtu 1484 + options=400 + ether 2e:ba:e4:83:4b:b7 + inet6 fe80::2cba:e4ff:fe83:4bb7%awdl0 prefixlen 64 scopeid 0x9 + nd6 options=201 + media: autoselect + status: active +llw0: flags=8863 mtu 1500 + options=400 + ether 2e:ba:e4:83:4b:b7 + inet6 fe80::2cba:e4ff:fe83:4bb7%llw0 prefixlen 64 scopeid 0xa + nd6 options=201 + media: aumm,l.j hn toselect + status: active +utun0: flags=8051 mtu 1380 + inet6 fe80::ba29:a797:6aa:9eb%utun0 prefixlen 64 scopeid 0xb + nd6 options=201 +utun1: flags=8051 mtu 2000 + inet6 fe80::64c6:1885:bec0:c316%utun1 prefixlen 64 scopeid 0xc + nd6 options=201 +utun2: flags=8051 mtu 1500 + inet 10.16.132.213 --> 10.16.132.213 netmask 0xfffffe00 +utun3: flags=8051 mtu 1500 + inet6 2001:db8:cafe::132:213 --> 2001:db8:cafe::132:213 prefixlen 128 +ib0: flags=4099 mtu 4092 + infiniband 80:00:02:08:FA:81:00:00:00:00:00:00:00:00:00:00:00:00:00:00 txqueuelen 256 (InfiniBand) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +ib1: flags=4099 mtu 4092 + infiniband 80:00:02:09:FA:81:00:00:00:00:00:00:00:00:00:00:00:00:00:00 txqueuelen 256 (InfiniBand) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + diff --git a/tests/samples/third_party/facter/ip_-6_route_show b/tests/samples/third_party/facter/ip_-6_route_show new file mode 100644 index 0000000..4bedf45 --- /dev/null +++ b/tests/samples/third_party/facter/ip_-6_route_show @@ -0,0 +1 @@ +fe80::/64 dev ens160 proto kernel metric 256 pref medium \ No newline at end of file diff --git a/tests/samples/third_party/facter/ip_link_show b/tests/samples/third_party/facter/ip_link_show new file mode 100644 index 0000000..51bcfb4 --- /dev/null +++ b/tests/samples/third_party/facter/ip_link_show @@ -0,0 +1,4 @@ +1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 +2: ens160: mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 + link/ether 00:50:56:9a:cb:a4 brd ff:ff:ff:ff:ff:ff \ No newline at end of file diff --git a/tests/samples/third_party/facter/ip_link_show_all b/tests/samples/third_party/facter/ip_link_show_all new file mode 100644 index 0000000..1568624 --- /dev/null +++ b/tests/samples/third_party/facter/ip_link_show_all @@ -0,0 +1,6 @@ +1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000\ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 +2: ens160: mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000\ link/ether 00:50:56:9a:51:af brd ff:ff:ff:ff:ff:ff +3: ib0: mtu 4092 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 256\ link/infiniband 80:00:02:08:fa:81:00:00:00:00:00:00:00:23:7d:ff:ff:94:73:fd brd 00:ff:ff:ff:ff:12:40:1b:ff:ff:00:00:00:00:00:00:ff:ff:ff:ff +4: eth2: mtu 1500 qdisc fq_codel master bond0 state UP mode DEFAULT group default qlen 1000\ link/ether 08:00:27:29:dc:a5 brd ff:ff:ff:ff:ff:ff +5: eth3: mtu 1500 qdisc fq_codel master bond0 state UP mode DEFAULT group default qlen 1000\ link/ether 08:00:27:29:dc:a5 brd ff:ff:ff:ff:ff:ff +6: bond0: mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000\ link/ether 08:00:27:29:dc:a5 brd ff:ff:ff:ff:ff:ff \ No newline at end of file diff --git a/tests/samples/third_party/facter/ip_route_show b/tests/samples/third_party/facter/ip_route_show new file mode 100644 index 0000000..b479db0 --- /dev/null +++ b/tests/samples/third_party/facter/ip_route_show @@ -0,0 +1,3 @@ +default via 10.16.112.1 dev ens192 proto dhcp src 10.16.125.217 metric 100 +10.16.112.0/20 dev ens192 proto kernel scope link src 10.16.125.217 +10.16.112.1 dev ens192 proto dhcp scope link src 10.16.125.217 metric 100 \ No newline at end of file diff --git a/tests/samples/third_party/facter/ip_route_show_default b/tests/samples/third_party/facter/ip_route_show_default new file mode 100644 index 0000000..ab35ddf --- /dev/null +++ b/tests/samples/third_party/facter/ip_route_show_default @@ -0,0 +1,2 @@ +10.16.112.0/20 dev ens160 proto kernel scope link src 10.16.124.1 +default via 10.16.112.1 dev ens160 diff --git a/tests/samples/third_party/facter/linux_ip_show_addr b/tests/samples/third_party/facter/linux_ip_show_addr new file mode 100644 index 0000000..735f181 --- /dev/null +++ b/tests/samples/third_party/facter/linux_ip_show_addr @@ -0,0 +1,20 @@ +1: lo: mtu 16436 qdisc noqueue state UNKNOWN + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever +2: dummy0: mtu 1500 qdisc noop state DOWN + link/ether e2:96:85:ac:40:24 brd ff:ff:ff:ff:ff:ff +3: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 + link/ether 4c:72:b9:24:7d:28 brd ff:ff:ff:ff:ff:ff + inet 198.245.51.174/24 brd 198.245.51.255 scope global eth0 + inet6 2607:5300:60:6ae::1/64 scope global + valid_lft forever preferred_lft forever + inet6 fe80::4e72:b9ff:fe24:7d28/64 scope link + valid_lft forever preferred_lft forever +4: tunl0: mtu 1480 qdisc noop state DOWN + link/ipip 0.0.0.0 brd 0.0.0.0 +5: sit0: mtu 1480 qdisc noop state DOWN + link/sit 0.0.0.0 brd 0.0.0.0 +6: ip6tnl0: mtu 1452 qdisc noop state DOWN + link/tunnel6 :: brd :: \ No newline at end of file diff --git a/tests/samples/third_party/facter/netstat/centos_5_5 b/tests/samples/third_party/facter/netstat/centos_5_5 new file mode 100644 index 0000000..2c46581 --- /dev/null +++ b/tests/samples/third_party/facter/netstat/centos_5_5 @@ -0,0 +1,5 @@ +Kernel IP routing table +Destination Gateway Genmask Flags MSS Window irtt Iface +100.100.100.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 +169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0 +0.0.0.0 100.100.100.1 0.0.0.0 UG 0 0 0 eth0 diff --git a/tests/samples/third_party/facter/netstat/darwin_10_3_0 b/tests/samples/third_party/facter/netstat/darwin_10_3_0 new file mode 100644 index 0000000..0aa6f4c --- /dev/null +++ b/tests/samples/third_party/facter/netstat/darwin_10_3_0 @@ -0,0 +1,35 @@ +Routing tables + +Internet: +Destination Gateway Flags Refs Use Netif Expire +default 100.100.104.1 UGSc 21 0 en0 +100.100.104/24 link#4 UCS 11 0 en0 +100.100.104.1 0:26:f1:23:2d:0 UHLWI 21 70927 en0 1188 +100.100.104.12 127.0.0.1 UHS 2 109 lo0 +100.100.104.16 0:1d:4f:44:cf:8 UHLWI 0 1369 en0 700 +100.100.104.48 0:1d:4f:45:5a:8c UHLWI 0 2 en0 1173 +100.100.104.52 0:1d:4f:44:ef:e0 UHLWI 0 184 en0 543 +100.100.104.62 0:1d:4f:45:5f:8c UHLWI 0 4 en0 478 +100.100.104.67 0:1d:4f:45:97:2c UHLWI 0 75 en0 851 +100.100.104.71 0:1d:4f:45:4a:b4 UHLWI 0 12 en0 890 +100.100.104.76 0:1d:4f:45:99:a8 UHLWI 0 2 en0 1056 +100.100.104.154 0:25:64:c1:d6:35 UHLWI 0 2640 en0 1200 +100.100.104.157 0:25:64:c1:cf:67 UHLWI 0 1 en0 1054 +100.100.104.255 ff:ff:ff:ff:ff:ff UHLWbI 0 2 en0 +127 127.0.0.1 UCS 0 0 lo0 +127.0.0.1 127.0.0.1 UH 0 10 lo0 +169.254 link#4 UCS 0 0 en0 +172.16.95/24 link#7 UC 0 0 vmnet8 +172.16.201/24 link#8 UC 0 0 vmnet1 + +Internet6: +Destination Gateway Flags Netif Expire +::1 ::1 UH lo0 +fe80::%lo0/64 fe80::1%lo0 Uc lo0 +fe80::1%lo0 link#1 UHL lo0 +fe80::%en0/64 link#4 UC en0 +fe80::217:f2ff:fe06:e3c2%en0 0:17:f2:6:e3:c2 UHL lo0 +ff01::/32 ::1 Um lo0 +ff02::/32 ::1 UmC lo0 +ff02::/32 link#4 UmC en0 +ff02::fb link#4 UHmLW en0 diff --git a/tests/samples/third_party/facter/netstat/darwin_10_6_4 b/tests/samples/third_party/facter/netstat/darwin_10_6_4 new file mode 100644 index 0000000..ad1537e --- /dev/null +++ b/tests/samples/third_party/facter/netstat/darwin_10_6_4 @@ -0,0 +1,29 @@ +Routing tables + +Internet: +Destination Gateway Flags Refs Use Netif Expire +default imana.puppetlabs.l UGSc 18 0 en1 +127 localhost UCS 0 0 lo0 +localhost localhost UH 2 161629 lo0 +169.254 link#5 UCS 0 0 en1 +172.16.38/24 link#9 UC 1 0 vmnet8 +172.16.38.255 ff:ff:ff:ff:ff:ff UHLWbI 1 2 vmnet8 +172.16.135/24 link#8 UC 1 0 vmnet1 +172.16.135.255 ff:ff:ff:ff:ff:ff UHLWbI 0 2 vmnet1 +192.168.100 link#5 UCS 17 0 en1 +imana.puppetlabs.l 0:25:bc:e2:e:56 UHLWI 9 12 en1 1145 +odys-iphone.puppet 5c:59:48:16:37:86 UHLWI 0 0 en1 1159 +reinh.puppetlabs.l localhost UHS 0 0 lo0 +192.168.100.255 ff:ff:ff:ff:ff:ff UHLWbI 0 2 en1 + +Internet6: +Destination Gateway Flags Netif Expire +localhost localhost UH lo0 +fe80::%lo0 localhost Uc lo0 +localhost link#1 UHL lo0 +fe80::%en1 link#5 UC en1 +fe80::226:b0ff:fef 0:26:b0:f9:ef:cb UHLW en1 +reinh.local 58:b0:35:7f:25:b3 UHL lo0 +ff01:: localhost Um lo0 +ff02:: localhost UmC lo0 +ff02:: link#5 UmC en1 diff --git a/tests/samples/third_party/facter/netstat/darwin_10_6_6_dualstack b/tests/samples/third_party/facter/netstat/darwin_10_6_6_dualstack new file mode 100644 index 0000000..14d937d --- /dev/null +++ b/tests/samples/third_party/facter/netstat/darwin_10_6_6_dualstack @@ -0,0 +1,34 @@ +Routing tables + +Internet: +Destination Gateway Flags Refs Use Netif Expire +default 192.168.1.254 UGSc 38 0 en1 +127 127.0.0.1 UCS 0 0 lo0 +127.0.0.1 127.0.0.1 UH 14 1044474 lo0 +169.254 link#5 UCS 0 0 en1 +192.168.1 link#5 UCS 2 0 en1 +192.168.1.207 127.0.0.1 UHS 0 1 lo0 +192.168.1.220 e0:f8:47:98:85:71 UHLWI 0 0 en1 135 +192.168.1.254 0:4:ed:66:13:cc UHLWI 42 134 en1 1158 + +Internet6: +Destination Gateway Flags Netif Expire +default 2000:44b4:61::16e UGSc tun0 +::1 ::1 UH lo0 +2000:44b4:61::16e 2000:44b4:61::16f UH tun0 +2000:44b4:61::16f link#9 UHL lo0 +2000:44b4:62:480::/64 link#5 UC en1 +2000:44b4:62:480::/60 ::1 UGSc lo0 +2000:44b4:62:480::1 0:25:0:48:19:ef UHL lo0 +fe80::%lo0/64 fe80::1%lo0 Uc lo0 +fe80::1%lo0 link#1 UHL lo0 +fe80::%en1/64 link#5 UC en1 +fe80::225:ff:fe48:19ef%en1 0:25:0:48:19:ef UHL lo0 +fe80::a00:27ff:fe02:bcb5%en1 8:0:27:2:bc:b5 UHLW en1 +fe80::a932:c76f:9c2e:ead8%en1 0:1e:2a:b3:9b:66 UHLW en1 +fe80::e2f8:47ff:fe98:8571%en1 e0:f8:47:98:85:71 UHLW en1 +fe80::225:4bff:feca:5672%tun0 link#9 UHL lo0 +ff01::/32 ::1 Um lo0 +ff02::/32 ::1 UmC lo0 +ff02::/32 link#5 UmC en1 +ff02::/32 fe80::225:4bff:feca:5672%tun0 UmC tun0 diff --git a/tests/samples/third_party/facter/netstat/darwin_9_8_0 b/tests/samples/third_party/facter/netstat/darwin_9_8_0 new file mode 100644 index 0000000..a8b8a50 --- /dev/null +++ b/tests/samples/third_party/facter/netstat/darwin_9_8_0 @@ -0,0 +1,28 @@ +Routing tables + +Internet: +Destination Gateway Flags Refs Use Netif Expire +default 100.100.107.1 UGSc 25 45789 en0 +100.100.107/24 link#4 UCS 3 0 en0 +100.100.107.1 0:26:f1:23:2d:0 UHLW 25 212733 en0 687 +100.100.107.4 127.0.0.1 UHS 0 332 lo0 +100.100.107.6 0:17:f2:6:e3:b0 UHLW 0 633 en0 1168 +100.100.107.255 ff:ff:ff:ff:ff:ff UHLWb 0 6 en0 +127 127.0.0.1 UCS 0 0 lo0 +127.0.0.1 127.0.0.1 UH 3 230 lo0 +169.254 link#4 UCS 0 0 en0 +192.168.61 link#8 UC 1 0 vmnet1 +192.168.61.255 ff:ff:ff:ff:ff:ff UHLWb 0 6 vmnet1 +192.168.210 link#7 UC 1 0 vmnet8 +192.168.210.255 ff:ff:ff:ff:ff:ff UHLWb 0 6 vmnet8 + +Internet6: +Destination Gateway Flags Netif Expire +::1 link#1 UHL lo0 +fe80::%lo0/64 fe80::1%lo0 Uc lo0 +fe80::1%lo0 link#1 UHL lo0 +fe80::%en0/64 link#4 UC en0 +fe80::217:f2ff:fe06:e42e%en0 0:17:f2:6:e4:2e UHL lo0 +ff01::/32 ::1 U lo0 +ff02::/32 fe80::1%lo0 UC lo0 +ff02::/32 link#4 UC en0 diff --git a/tests/samples/third_party/facter/netstat/fedora_10 b/tests/samples/third_party/facter/netstat/fedora_10 new file mode 100644 index 0000000..33d3f4e --- /dev/null +++ b/tests/samples/third_party/facter/netstat/fedora_10 @@ -0,0 +1,7 @@ +Kernel IP routing table +Destination Gateway Genmask Flags MSS Window irtt Iface +172.16.131.0 0.0.0.0 255.255.255.0 U 0 0 0 vmnet1 +192.168.183.0 0.0.0.0 255.255.255.0 U 0 0 0 vmnet8 +100.100.108.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 +169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0 +0.0.0.0 100.100.108.1 0.0.0.0 UG 0 0 0 eth0 diff --git a/tests/samples/third_party/facter/netstat/open_solaris_10 b/tests/samples/third_party/facter/netstat/open_solaris_10 new file mode 100644 index 0000000..c211704 --- /dev/null +++ b/tests/samples/third_party/facter/netstat/open_solaris_10 @@ -0,0 +1,16 @@ +Routing Table: IPv4 + Destination Gateway Flags Ref Use Interface +-------------------- -------------------- ----- ----- ---------- --------- +default 192.168.2.1 UG 1 10 +192.168.2.0 192.168.2.83 U 1 1022 hme0 +224.0.0.0 192.168.2.83 U 1 0 hme0 +127.0.0.1 127.0.0.1 UH 1 0 lo0 + +Routing Table: IPv6 + Destination/Mask Gateway Flags Ref Use If +--------------------------- --------------------------- ----- --- ------- ----- +2404:130:0:1000::/64 2404:130:0:1000:a00:20ff:fed1:6d79 U 1 0 hme0:1 +fe80::/10 fe80::a00:20ff:fed1:6d79 U 1 0 hme0 +ff00::/8 fe80::a00:20ff:fed1:6d79 U 1 0 hme0 +default fe80::214:22ff:fe0a:1c46 UG 1 0 hme0 +::1 ::1 UH 1 140 lo0 diff --git a/tests/samples/third_party/facter/netstat/open_solaris_b132 b/tests/samples/third_party/facter/netstat/open_solaris_b132 new file mode 100644 index 0000000..9a7ed89 --- /dev/null +++ b/tests/samples/third_party/facter/netstat/open_solaris_b132 @@ -0,0 +1,17 @@ +Routing Table: IPv4 + Destination Gateway Flags Ref Use Interface +-------------------- -------------------- ----- ----- ---------- --------- +default 202.78.241.102 UG 4 56215415 +127.0.0.1 127.0.0.1 UH 2 15396 lo0 +172.31.255.0 172.31.255.254 U 12 62487076 int0 +202.78.241.96 202.78.241.97 U 6 2213722 bge0 + +Routing Table: IPv6 + Destination/Mask Gateway Flags Ref Use If +--------------------------- --------------------------- ----- --- ------- ----- +::1 ::1 UH 2 4 lo0 +2404:130:40:10::/64 2404:130:40:10::ff:0 U 4 1050430 bge0 +2404:130:40:18::/64 2404:130:40:18::ff:0 U 4 1302106 int0 +fe80::/10 fe80::ff:0 U 3 16283 int0 +fe80::/10 fe80::ff:0 U 5 314822 bge0 +default fe80::20d:edff:fe9d:782e UG 2 420100 bge0 diff --git a/tests/samples/third_party/facter/netstat/ubuntu_7_04 b/tests/samples/third_party/facter/netstat/ubuntu_7_04 new file mode 100644 index 0000000..4f49311 --- /dev/null +++ b/tests/samples/third_party/facter/netstat/ubuntu_7_04 @@ -0,0 +1,7 @@ +Kernel IP routing table +Destination Gateway Genmask Flags MSS Window irtt Iface +100.100.106.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 +169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 ath0 +169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0 +0.0.0.0 100.100.106.1 0.0.0.0 UG 0 0 0 eth0 +0.0.0.0 0.0.0.0 0.0.0.0 U 0 0 0 ath0 diff --git a/tests/samples/third_party/facter/netstat_in b/tests/samples/third_party/facter/netstat_in new file mode 100644 index 0000000..8f604ac --- /dev/null +++ b/tests/samples/third_party/facter/netstat_in @@ -0,0 +1,6 @@ +Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll +en0 1500 link#2 a.c6.24.39.41.3 162325 0 60252 0 0 +en0 1500 10.32.77 10.32.77.30 162325 0 60252 0 0 +lo0 16896 link#1 36891 0 36891 0 0 +lo0 16896 127 127.0.0.1 36891 0 36891 0 0 +lo0 16896 ::1%1 36891 0 36891 0 0 \ No newline at end of file diff --git a/tests/samples/third_party/facter/netstat_rn b/tests/samples/third_party/facter/netstat_rn new file mode 100644 index 0000000..4cb5062 --- /dev/null +++ b/tests/samples/third_party/facter/netstat_rn @@ -0,0 +1,13 @@ +Routing tables +Destination Gateway Flags Refs Use If Exp Groups + +Route Tree for Protocol Family 2 (Internet): +default 10.32.77.1 UG 1 12696 en0 - - +10.32.77.0 10.32.77.40 UHSb 0 0 en0 - - => +10.32.77/24 10.32.77.40 U 0 639 en0 - - +10.32.77.40 127.0.0.1 UGHS 0 16 lo0 - - +10.32.77.255 10.32.77.40 UHSb 0 1 en0 - - +127/8 127.0.0.1 U 1 7504 lo0 - - + +Route Tree for Protocol Family 24 (Internet v6): +::1%1 ::1%1 UH 2 798 lo0 - - \ No newline at end of file diff --git a/tests/samples/third_party/facter/netstat_rn_two b/tests/samples/third_party/facter/netstat_rn_two new file mode 100644 index 0000000..0ac062a --- /dev/null +++ b/tests/samples/third_party/facter/netstat_rn_two @@ -0,0 +1,6 @@ +Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll +en0 1500 link#2 e2.69.92.64.40.2 54611 0 11532 0 0 +en0 1500 10.16.78 10.16.78.58 54611 0 11532 0 0 +lo0* 16896 link#1 2237 0 2237 0 0 +lo0* 16896 127 127.0.0.1 2237 0 2237 0 0 +lo0* 16896 ::1%1 2237 0 2237 0 0 \ No newline at end of file diff --git a/tests/samples/third_party/facter/proc_net_route b/tests/samples/third_party/facter/proc_net_route new file mode 100644 index 0000000..a2ad7c9 --- /dev/null +++ b/tests/samples/third_party/facter/proc_net_route @@ -0,0 +1,3 @@ +Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT +ens160 00000000 0170100A 0003 0 0 0 00000000 0 0 0 +ens160 0070100A 00000000 0001 0 0 0 00F0FFFF 0 0 0 diff --git a/tests/samples/third_party/facter/proc_net_route_empty b/tests/samples/third_party/facter/proc_net_route_empty new file mode 100644 index 0000000..a7bf419 --- /dev/null +++ b/tests/samples/third_party/facter/proc_net_route_empty @@ -0,0 +1 @@ +Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT diff --git a/tests/samples/third_party/facter/route_n_get_default b/tests/samples/third_party/facter/route_n_get_default new file mode 100644 index 0000000..476816e --- /dev/null +++ b/tests/samples/third_party/facter/route_n_get_default @@ -0,0 +1,8 @@ + route to: default +destination: default + mask: default + gateway: 10.16.112.1 + interface: net0 + flags: + recvpipe sendpipe ssthresh rtt,ms rttvar,ms hopcount mtu expire + 0 0 0 0 0 0 1500 0 diff --git a/tests/samples/third_party/facter/solaris_ifconfig b/tests/samples/third_party/facter/solaris_ifconfig new file mode 100644 index 0000000..df279de --- /dev/null +++ b/tests/samples/third_party/facter/solaris_ifconfig @@ -0,0 +1,3 @@ +net0: flags=100001004843 mtu 1500 index 2 + inet 10.16.115.67 netmask fffff000 broadcast 10.16.127.255 + ether 0:50:56:9a:45:1c diff --git a/tests/samples/third_party/facter/windows_netsh_addresses_with_multiple_interfaces b/tests/samples/third_party/facter/windows_netsh_addresses_with_multiple_interfaces new file mode 100644 index 0000000..6235f8e --- /dev/null +++ b/tests/samples/third_party/facter/windows_netsh_addresses_with_multiple_interfaces @@ -0,0 +1,35 @@ +Address ::1 Parameters +--------------------------------------------------------- +Interface Luid : Loopback Pseudo-Interface 1 +Scope Id : 0.0 +Valid Lifetime : infinite +Preferred Lifetime : infinite +DAD State : Preferred +Address Type : Other + +Address fe80::7128:aa90:8f2a:8375%9 Parameters +--------------------------------------------------------- +Interface Luid : Local Area Connection +Scope Id : 0.9 +Valid Lifetime : infinite +Preferred Lifetime : infinite +DAD State : Preferred +Address Type : Other + +Address fe80::5efe:172.16.138.216%11 Parameters +--------------------------------------------------------- +Interface Luid : isatap.localdomain +Scope Id : 0.11 +Valid Lifetime : infinite +Preferred Lifetime : infinite +DAD State : Deprecated +Address Type : Other + +Address 2001:0:4137:9e76:2087:77a:53ef:7527 Parameters +--------------------------------------------------------- +Interface Luid : Teredo Tunneling Pseudo-Interface +Scope Id : 0.0 +Valid Lifetime : infinite +Preferred Lifetime : infinite +DAD State : Preferred +Address Type : Public diff --git a/tests/samples/third_party/facter/windows_netsh_all_interfaces b/tests/samples/third_party/facter/windows_netsh_all_interfaces new file mode 100644 index 0000000..ed9a83a --- /dev/null +++ b/tests/samples/third_party/facter/windows_netsh_all_interfaces @@ -0,0 +1,12 @@ +Idx Met MTU State Name +--- ---------- ---------- ------------ --------------------------- + 1 50 4294967295 connected Loopback Pseudo-Interface 1 + 9 10 1500 connected Local Area Connection + + +Idx Met MTU State Name +--- ---------- ---------- ------------ --------------------------- + 1 50 4294967295 connected Loopback Pseudo-Interface 1 + 9 10 1500 connected Local Area Connection + 11 50 1280 disconnected isatap.localdomain + 12 50 1280 connected Teredo Tunneling Pseudo-Interface diff --git a/tests/samples/third_party/facter/windows_netsh_single_interface b/tests/samples/third_party/facter/windows_netsh_single_interface new file mode 100644 index 0000000..f6a8b2b --- /dev/null +++ b/tests/samples/third_party/facter/windows_netsh_single_interface @@ -0,0 +1,7 @@ +Configuration for interface "Local Area Connection" + DHCP enabled: Yes + IP Address: 172.16.138.216 + Subnet Prefix: 172.16.138.0/24 (mask 255.255.255.0) + Default Gateway: 172.16.138.2 + Gateway Metric: 0 + InterfaceMetric: 10 diff --git a/tests/samples/third_party/facter/windows_netsh_single_interface6 b/tests/samples/third_party/facter/windows_netsh_single_interface6 new file mode 100644 index 0000000..504d226 --- /dev/null +++ b/tests/samples/third_party/facter/windows_netsh_single_interface6 @@ -0,0 +1,18 @@ + +Address fe80::2087:77a:53ef:7527%12 Parameters +--------------------------------------------------------- +Interface Luid : Teredo Tunneling Pseudo-Interface +Scope Id : 0.12 +Valid Lifetime : infinite +Preferred Lifetime : infinite +DAD State : Preferred +Address Type : Other + +Address 2001:0:4137:9e76:2087:77a:53ef:7527 Parameters +--------------------------------------------------------- +Interface Luid : Teredo Tunneling Pseudo-Interface +Scope Id : 0.0 +Valid Lifetime : infinite +Preferred Lifetime : infinite +DAD State : Preferred +Address Type : Public From 7180d1b5b328270121cbc729326e04cd972356f7 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 23:11:04 -0700 Subject: [PATCH 101/112] test: add samples from GLPI Agent project --- tests/samples/third_party/glpi_agent/LICENSE | 339 ++++++++++++++++++ .../samples/third_party/glpi_agent/README.md | 1 + .../third_party/glpi_agent/generic/README | 13 + .../third_party/glpi_agent/generic/arp/linux | 1 + .../glpi_agent/generic/arp/linux-ip-neighbor | 1 + .../third_party/glpi_agent/generic/arp/none | 1 + .../third_party/glpi_agent/generic/arp/win32 | 4 + .../glpi_agent/generic/ifconfig/aix-5.3 | 1 + .../glpi_agent/generic/ifconfig/aix-6.1 | 1 + .../glpi_agent/generic/ifconfig/dell-xt2 | 42 +++ .../glpi_agent/generic/ifconfig/dragonfly-1 | 15 + .../glpi_agent/generic/ifconfig/freebsd-4 | 16 + .../glpi_agent/generic/ifconfig/freebsd-8.1 | 24 ++ .../glpi_agent/generic/ifconfig/freebsd-bis | 20 ++ .../glpi_agent/generic/ifconfig/freebsd-ter | 35 ++ .../glpi_agent/generic/ifconfig/hpux1-lan0 | 2 + .../glpi_agent/generic/ifconfig/hpux2-lan0 | 2 + .../generic/ifconfig/linux-archlinux | 25 ++ .../glpi_agent/generic/ifconfig/linux-bonding | 31 ++ .../glpi_agent/generic/ifconfig/linux-el8 | 58 +++ .../glpi_agent/generic/ifconfig/linux-fc17 | 10 + .../glpi_agent/generic/ifconfig/linux-rhel5.6 | 83 +++++ .../glpi_agent/generic/ifconfig/oi-2021.10 | 10 + .../glpi_agent/generic/ifconfig/opensolaris | 12 + .../glpi_agent/generic/ifconfig/solaris-10 | 20 ++ .../glpi_agent/generic/netstat/aix-5.3a | 14 + .../glpi_agent/generic/netstat/aix-5.3c | 27 ++ .../glpi_agent/generic/netstat/aix-6.1a | 19 + .../glpi_agent/generic/netstat/aix-6.1b | 31 ++ .../glpi_agent/generic/netstat/hpux1 | 9 + .../glpi_agent/generic/netstat/hpux2 | 12 + .../glpi_agent/generic/netstat/linux1 | 5 + .../glpi_agent/generic/netstat/macosx1 | 11 + .../glpi_agent/generic/netstat/netbsd | 11 + .../glpi_agent/generic/netstat/openbsd | 12 + .../third_party/glpi_agent/hpux/README | 60 ++++ .../glpi_agent/hpux/lanadmin/hpux1-0 | 41 +++ .../glpi_agent/hpux/lanadmin/hpux1-1 | 41 +++ .../glpi_agent/hpux/lanadmin/hpux2-0 | 41 +++ .../glpi_agent/hpux/lanadmin/hpux2-1 | 41 +++ .../third_party/glpi_agent/hpux/lanscan/hpux | 42 +++ .../third_party/glpi_agent/hpux/lanscan/hpux1 | 2 + .../third_party/glpi_agent/hpux/lanscan/hpux2 | 7 + .../third_party/glpi_agent/hpux/netstat/hpux | 7 + .../third_party/glpi_agent/hpux/netstat/hpux1 | 9 + .../third_party/glpi_agent/hpux/netstat/hpux2 | 7 + .../third_party/glpi_agent/hpux/netstat/hpux3 | 11 + .../third_party/glpi_agent/hpux/nwmgr/sample1 | 27 ++ .../third_party/glpi_agent/hpux/nwmgr/sample2 | 27 ++ .../third_party/glpi_agent/linux/README | 30 ++ .../glpi_agent/linux/ip/default-gateway-1 | 1 + .../glpi_agent/linux/ip/default-gateway-2 | 2 + .../glpi_agent/linux/ip/default-gateway-3 | 2 + .../third_party/glpi_agent/linux/ip/ip_addr-1 | 25 ++ .../third_party/glpi_agent/linux/ip/ip_addr-2 | 17 + .../third_party/glpi_agent/linux/ip/ip_addr-3 | 12 + .../third_party/glpi_agent/linux/ip/ip_addr-4 | 3 + .../third_party/glpi_agent/linux/ip/ip_addr-5 | 22 ++ .../glpi_agent/linux/ip/ip_addr-el8 | 26 ++ 59 files changed, 1421 insertions(+) create mode 100644 tests/samples/third_party/glpi_agent/LICENSE create mode 100644 tests/samples/third_party/glpi_agent/README.md create mode 100644 tests/samples/third_party/glpi_agent/generic/README create mode 100644 tests/samples/third_party/glpi_agent/generic/arp/linux create mode 100644 tests/samples/third_party/glpi_agent/generic/arp/linux-ip-neighbor create mode 100644 tests/samples/third_party/glpi_agent/generic/arp/none create mode 100644 tests/samples/third_party/glpi_agent/generic/arp/win32 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/aix-5.3 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/aix-6.1 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/dell-xt2 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/dragonfly-1 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-4 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-8.1 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-bis create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-ter create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/hpux1-lan0 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/hpux2-lan0 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/linux-archlinux create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/linux-bonding create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/linux-el8 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/linux-fc17 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/linux-rhel5.6 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/oi-2021.10 create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/opensolaris create mode 100644 tests/samples/third_party/glpi_agent/generic/ifconfig/solaris-10 create mode 100644 tests/samples/third_party/glpi_agent/generic/netstat/aix-5.3a create mode 100644 tests/samples/third_party/glpi_agent/generic/netstat/aix-5.3c create mode 100644 tests/samples/third_party/glpi_agent/generic/netstat/aix-6.1a create mode 100644 tests/samples/third_party/glpi_agent/generic/netstat/aix-6.1b create mode 100644 tests/samples/third_party/glpi_agent/generic/netstat/hpux1 create mode 100644 tests/samples/third_party/glpi_agent/generic/netstat/hpux2 create mode 100644 tests/samples/third_party/glpi_agent/generic/netstat/linux1 create mode 100644 tests/samples/third_party/glpi_agent/generic/netstat/macosx1 create mode 100644 tests/samples/third_party/glpi_agent/generic/netstat/netbsd create mode 100644 tests/samples/third_party/glpi_agent/generic/netstat/openbsd create mode 100644 tests/samples/third_party/glpi_agent/hpux/README create mode 100644 tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux1-0 create mode 100644 tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux1-1 create mode 100644 tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux2-0 create mode 100644 tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux2-1 create mode 100644 tests/samples/third_party/glpi_agent/hpux/lanscan/hpux create mode 100644 tests/samples/third_party/glpi_agent/hpux/lanscan/hpux1 create mode 100644 tests/samples/third_party/glpi_agent/hpux/lanscan/hpux2 create mode 100644 tests/samples/third_party/glpi_agent/hpux/netstat/hpux create mode 100644 tests/samples/third_party/glpi_agent/hpux/netstat/hpux1 create mode 100644 tests/samples/third_party/glpi_agent/hpux/netstat/hpux2 create mode 100644 tests/samples/third_party/glpi_agent/hpux/netstat/hpux3 create mode 100644 tests/samples/third_party/glpi_agent/hpux/nwmgr/sample1 create mode 100644 tests/samples/third_party/glpi_agent/hpux/nwmgr/sample2 create mode 100644 tests/samples/third_party/glpi_agent/linux/README create mode 100644 tests/samples/third_party/glpi_agent/linux/ip/default-gateway-1 create mode 100644 tests/samples/third_party/glpi_agent/linux/ip/default-gateway-2 create mode 100644 tests/samples/third_party/glpi_agent/linux/ip/default-gateway-3 create mode 100644 tests/samples/third_party/glpi_agent/linux/ip/ip_addr-1 create mode 100644 tests/samples/third_party/glpi_agent/linux/ip/ip_addr-2 create mode 100644 tests/samples/third_party/glpi_agent/linux/ip/ip_addr-3 create mode 100644 tests/samples/third_party/glpi_agent/linux/ip/ip_addr-4 create mode 100644 tests/samples/third_party/glpi_agent/linux/ip/ip_addr-5 create mode 100644 tests/samples/third_party/glpi_agent/linux/ip/ip_addr-el8 diff --git a/tests/samples/third_party/glpi_agent/LICENSE b/tests/samples/third_party/glpi_agent/LICENSE new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/tests/samples/third_party/glpi_agent/README.md b/tests/samples/third_party/glpi_agent/README.md new file mode 100644 index 0000000..1cc372e --- /dev/null +++ b/tests/samples/third_party/glpi_agent/README.md @@ -0,0 +1 @@ +The files in this folder are from the GPLI Agent project (https://github.com/glpi-project/glpi-agent) and are licensed with the GPLv2 license. The files used are unmodified and only used as input data for unit tests. diff --git a/tests/samples/third_party/glpi_agent/generic/README b/tests/samples/third_party/glpi_agent/generic/README new file mode 100644 index 0000000..74ecaba --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/README @@ -0,0 +1,13 @@ +generic command and file samples + +ifconfig +- aix-5.3: ifconfig -l +- aix-6.1: ifconfig -l +- hpux1-lan0: ifconfig lan0 +- hpux2-lan0: ifconfig lan0 + +netstat: +- hpux*: netstat -nr +- aix-*: netstat -nr +- *bsd: netstat -nr -f inet +- linux1: netstat -nr diff --git a/tests/samples/third_party/glpi_agent/generic/arp/linux b/tests/samples/third_party/glpi_agent/generic/arp/linux new file mode 100644 index 0000000..7910b4d --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/arp/linux @@ -0,0 +1 @@ +hostname.test (192.168.0.3) at 00:8d:b9:37:4a:c2 [ether] on eth0 diff --git a/tests/samples/third_party/glpi_agent/generic/arp/linux-ip-neighbor b/tests/samples/third_party/glpi_agent/generic/arp/linux-ip-neighbor new file mode 100644 index 0000000..70dccdb --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/arp/linux-ip-neighbor @@ -0,0 +1 @@ +10.0.10.1 dev enp3s0 lladdr 00:0d:b9:37:2b:c2 REACHABLE diff --git a/tests/samples/third_party/glpi_agent/generic/arp/none b/tests/samples/third_party/glpi_agent/generic/arp/none new file mode 100644 index 0000000..3348487 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/arp/none @@ -0,0 +1 @@ +Aucune entrée ARP trouvée. diff --git a/tests/samples/third_party/glpi_agent/generic/arp/win32 b/tests/samples/third_party/glpi_agent/generic/arp/win32 new file mode 100644 index 0000000..12db058 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/arp/win32 @@ -0,0 +1,4 @@ + +Interface?: 192.168.0.107 --- 0xa + Adresse Internet Adresse physique Type + 192.168.0.1 00-80-0c-07-ae-d3 dynamique diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/aix-5.3 b/tests/samples/third_party/glpi_agent/generic/ifconfig/aix-5.3 new file mode 100644 index 0000000..b8edb0c --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/aix-5.3 @@ -0,0 +1 @@ +en0 lo0 diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/aix-6.1 b/tests/samples/third_party/glpi_agent/generic/ifconfig/aix-6.1 new file mode 100644 index 0000000..c4e9024 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/aix-6.1 @@ -0,0 +1 @@ +en0 en1 lo0 diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/dell-xt2 b/tests/samples/third_party/glpi_agent/generic/ifconfig/dell-xt2 new file mode 100644 index 0000000..8fecb34 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/dell-xt2 @@ -0,0 +1,42 @@ +eth0 Link encap:Ethernet HWaddr A4:BA:DB:A5:F5:FA + inet addr:192.168.0.5 Bcast:192.168.0.255 Mask:255.255.255.0 + inet6 addr: fe80::a6ba:dbff:fea5:f5fa/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:39818 errors:0 dropped:0 overruns:0 frame:0 + TX packets:36364 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:100 + RX bytes:24670758 (23.5 MiB) TX bytes:3555602 (3.3 MiB) + Memory:f6ae0000-f6b00000 + +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope:Host + UP LOOPBACK RUNNING MTU:16436 Metric:1 + RX packets:193 errors:0 dropped:0 overruns:0 frame:0 + TX packets:193 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:16572 (16.1 KiB) TX bytes:16572 (16.1 KiB) + +pan0 Link encap:Ethernet HWaddr 4E:8C:81:ED:9B:35 + BROADCAST MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) + +sit0 Link encap:IPv6-in-IPv4 + NOARP MTU:1480 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) + +wlan0 Link encap:Ethernet HWaddr 00:24:D6:6F:81:3A + inet addr:78.251.91.204 Bcast:78.251.127.255 Mask:255.255.192.0 + inet6 addr: fe80::224:d6ff:fe6f:813a/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:6 errors:0 dropped:0 overruns:0 frame:0 + TX packets:48 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:1452 (1.4 KiB) TX bytes:10231 (9.9 KiB) + diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/dragonfly-1 b/tests/samples/third_party/glpi_agent/generic/ifconfig/dragonfly-1 new file mode 100644 index 0000000..b1b0bda --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/dragonfly-1 @@ -0,0 +1,15 @@ +em0: flags=8843 mtu 1500 + options=1b + inet6 fe80::a00:27ff:fe2e:7097%em0 prefixlen 64 scopeid 0x1 + inet 10.0.1.117 netmask 0xffffff00 broadcast 10.0.1.255 + ether 08:00:27:2e:70:97 + media: Ethernet autoselect (1000baseT ) + status: active +faith0: flags=8002 mtu 1500 +lo0: flags=8049 mtu 16384 + options=3 + inet 127.0.0.1 netmask 0xff000000 + inet6 ::1 prefixlen 128 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x3 +ppp0: flags=8010 mtu 1500 +sl0: flags=c010 mtu 552 diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-4 b/tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-4 new file mode 100644 index 0000000..27dd289 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-4 @@ -0,0 +1,16 @@ +iwn0: flags=8843 metric 0 mtu 2290 + ether 3c:a9:f4:5a:04:b8 + nd6 options=21 + media: IEEE 802.11 Wireless Ethernet autoselect mode 11ng + status: associated +wlan0: flags=8843 metric 0 mtu 1500 + ether 3c:a9:f4:5a:04:b8 + inet 192.168.20.114 netmask 0xffffff00 broadcast 192.168.20.255 + nd6 options=29 + media: IEEE 802.11 Wireless Ethernet MCS mode 11ng + status: associated + ssid ciscosb-2 channel 1 (2412 MHz 11g ht/40+) bssid c6:64:13:c5:50:c7 + country US authmode OPEN privacy OFF txpower 14 bmiss 10 scanvalid 60 + bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 64 + protmode CTS ampdulimit 64k ampdudensity 8 -amsdutx amsdurx shortgi + wme \ No newline at end of file diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-8.1 b/tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-8.1 new file mode 100644 index 0000000..19c2f08 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-8.1 @@ -0,0 +1,24 @@ +re0: flags=8843 metric 0 mtu 1500 + options=389b + ether c8:0a:a9:3f:35:fa + inet 129.132.95.98 netmask 0xffffffc0 broadcast 129.132.95.127 + media: Ethernet autoselect (1000baseT ) + status: active +fwe0: flags=8802 metric 0 mtu 1500 + options=8 + ether 02:24:1b:9d:ca:01 + ch 1 dma -1 +fwip0: flags=8802 metric 0 mtu 1500 + lladdr 0.24.1b.0.96.9d.ca.1.a.2.ff.fe.0.0.0.0 +lo0: flags=8049 metric 0 mtu 16384 + options=3 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x4 + inet6 ::1 prefixlen 128 + inet 127.0.0.1 netmask 0xff000000 + nd6 options=3 +vboxnet0: flags=8802 metric 0 mtu 1500 + ether 0a:00:27:00:00:00 +tun0: flags=8051 metric 0 mtu 1500 + options=80000 + inet 192.168.200.6 --> 192.168.200.5 netmask 0xffffffff + Opened by PID 2006 diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-bis b/tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-bis new file mode 100644 index 0000000..385606c --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-bis @@ -0,0 +1,20 @@ +bce0: flags=8843 metric 0 mtu 1500 + options=19b + ether 00:16:18:87:ca:b5 + inet 11.105.11.105 netmask 0xffffffc0 broadcast 11.105.11.127 + inet 11.105.11.110 netmask 0xffffffff broadcast 11.105.11.110 + inet 11.105.11.111 netmask 0xffffffff broadcast 11.105.11.111 + media: Ethernet autoselect (1000baseT ) + status: active +bce1: flags=8843 metric 0 mtu 1500 + options=19b + ether 00:16:18:87:ca:b6 + inet 192.168.12.105 netmask 0xffffff00 broadcast 192.168.12.255 + media: Ethernet autoselect (1000baseT ) + status: active +lo0: flags=8049 metric 0 mtu 16384 + options=3 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x3 + inet6 ::1 prefixlen 128 + inet 127.0.0.1 netmask 0xff000000 + nd6 options=3 diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-ter b/tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-ter new file mode 100644 index 0000000..3917d0e --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/freebsd-ter @@ -0,0 +1,35 @@ +em0: flags=8843 metric 0 mtu 1500 + options=4219b + ether 00:23:18:cf:0d:93 + nd6 options=29 + media: Ethernet autoselect + status: no carrier +lo0: flags=8049 metric 0 mtu 16384 + options=600003 + inet6 ::1 prefixlen 128 + inet6 fe80::1%lo0 prefixlen 64 scopeid 0x4 + inet 127.0.0.1 netmask 0xff000000 + nd6 options=21 +lo1: flags=8049 metric 0 mtu 16384 + options=600003 + inet 10.0.0.254 netmask 0xffffff00 + inet 10.0.0.1 netmask 0xffffff00 + nd6 options=29 +ndis0: flags=8843 metric 0 mtu 2290 + ether 4c:ed:de:2c:9d:9a + nd6 options=29 + media: IEEE 802.11 Wireless Ethernet autoselect mode 11g + status: associated +wlan0: flags=8843 metric 0 mtu 1500 + ether 4c:ed:de:2c:9d:9a + inet 192.168.0.158 netmask 0xffffff00 broadcast 192.168.0.255 + nd6 options=29 + media: IEEE 802.11 Wireless Ethernet OFDM/48Mbps mode 11g + status: associated + ssid BZH channel 7 (2442 MHz 11g) bssid 00:07:cb:01:85:50 + country US authmode WPA privacy OFF powersavemode CAM + powersavesleep 100 txpower 0 bmiss 7 mcastrate 6 mgmtrate 6 + scanvalid 60 protmode CTS roaming MANUAL bintval 96 +vboxnet0: flags=8802 metric 0 mtu 1500 + ether 0a:00:27:00:00:00 + nd6 options=29 diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/hpux1-lan0 b/tests/samples/third_party/glpi_agent/generic/ifconfig/hpux1-lan0 new file mode 100644 index 0000000..56697a7 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/hpux1-lan0 @@ -0,0 +1,2 @@ +lan0: flags=1843 + inet 10.0.4.56 netmask ffffffe0 broadcast 10.0.4.63 diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/hpux2-lan0 b/tests/samples/third_party/glpi_agent/generic/ifconfig/hpux2-lan0 new file mode 100644 index 0000000..feed470 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/hpux2-lan0 @@ -0,0 +1,2 @@ +lan0: flags=1843 + inet 10.0.0.48 netmask ffffffe0 broadcast 10.0.0.63 diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-archlinux b/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-archlinux new file mode 100644 index 0000000..9758367 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-archlinux @@ -0,0 +1,25 @@ +eth0: flags=4163 mtu 1500 metric 1 + inet 192.168.2.43 netmask 255.255.255.0 broadcast 192.168.2.255 + inet6 fe80::4a5b:39ff:fec6:53ba prefixlen 64 scopeid 0x20 + ether 48:5b:39:c6:53:ba txqueuelen 1000 (Ethernet) + RX packets 20124909 bytes 3625717309 (3.3 GiB) + RX errors 0 dropped 82 overruns 0 frame 0 + TX packets 15743857 bytes 2221212850 (2.0 GiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + device interrupt 43 base 0x8000 + +lo: flags=73 mtu 16436 metric 1 + inet 127.0.0.1 netmask 255.0.0.0 + inet6 ::1 prefixlen 128 scopeid 0x10 + loop txqueuelen 0 (Local Loopback) + RX packets 69371 bytes 3675083 (3.5 MiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 69371 bytes 3675083 (3.5 MiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +vboxnet0: flags=4098 mtu 1500 metric 1 + ether 0a:00:27:00:00:00 txqueuelen 1000 (Ethernet) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-bonding b/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-bonding new file mode 100644 index 0000000..faff62f --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-bonding @@ -0,0 +1,31 @@ +bond0 Link encap:Ethernet HWaddr 00:50:56:AD:00:0E + inet addr:192.168.1.181 Bcast:192.168.1.255 Mask:255.255.255.0 + inet6 addr: fe80::250:56ff:fead:e/64 Scope:Link + UP BROADCAST RUNNING MASTER MULTICAST MTU:1500 Metric:1 + RX packets:123713 errors:0 dropped:0 overruns:0 frame:0 + TX packets:116904 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:35769841 (34.1 MiB) TX bytes:12452514 (11.8 MiB) + +eth0 Link encap:Ethernet HWaddr 00:50:56:AD:00:0E + UP BROADCAST RUNNING SLAVE MULTICAST MTU:1500 Metric:1 + RX packets:123713 errors:0 dropped:0 overruns:0 frame:0 + TX packets:116907 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:35769841 (34.1 MiB) TX bytes:12453016 (11.8 MiB) + +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope:Host + UP LOOPBACK RUNNING MTU:16436 Metric:1 + RX packets:483856 errors:0 dropped:0 overruns:0 frame:0 + TX packets:483856 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:48031984 (45.8 MiB) TX bytes:48031984 (45.8 MiB) + +sit0 Link encap:IPv6-in-IPv4 + NOARP MTU:1480 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-el8 b/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-el8 new file mode 100644 index 0000000..2eea73b --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-el8 @@ -0,0 +1,58 @@ +docker0: flags=4099 mtu 1500 + inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 + ether 02:42:0c:d5:0f:d7 txqueuelen 0 (Ethernet) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +eth0: flags=4163 mtu 1500 + ether e4:11:5b:ed:36:0c txqueuelen 1000 (Ethernet) + RX packets 27681443338 bytes 7773253411021 (7.0 TiB) + RX errors 9434 dropped 51838 overruns 0 frame 9434 + TX packets 34788825065 bytes 26406512352943 (24.0 TiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +eth1: flags=4098 mtu 1500 + ether e4:11:5b:ed:36:38 txqueuelen 1000 (Ethernet) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +eth2: flags=4098 mtu 1500 + ether e4:11:5b:ed:36:0e txqueuelen 1000 (Ethernet) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +eth3: flags=4098 mtu 1500 + ether e4:11:5b:ed:36:3a txqueuelen 1000 (Ethernet) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +eth0:srv: flags=4163 mtu 1500 + inet 10.10.220.100 netmask 255.255.255.255 broadcast 0.0.0.0 + ether e4:11:5b:ed:36:0c txqueuelen 1000 (Ethernet) + +lo: flags=73 mtu 65536 + inet 127.0.0.1 netmask 255.0.0.0 + inet6 ::1 prefixlen 128 scopeid 0x10 + loop txqueuelen 1000 (Local Loopback) + RX packets 137902154908 bytes 41129565562314 (37.4 TiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 137902154908 bytes 41129565562314 (37.4 TiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +macvlan0: flags=4163 mtu 1500 + inet 10.10.220.101 netmask 255.255.255.0 broadcast 10.10.220.255 + inet6 fe80::260f:e9bb:60de:58f4 prefixlen 64 scopeid 0x20 + ether 4e:05:62:03:69:e7 txqueuelen 1000 (Ethernet) + RX packets 25567695356 bytes 8625506639624 (7.8 TiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 23083998443 bytes 25613000107345 (23.2 TiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-fc17 b/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-fc17 new file mode 100644 index 0000000..39a18dc --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-fc17 @@ -0,0 +1,10 @@ +em1: flags=4163 mtu 1500 + inet 10.1.65.145 netmask 255.255.0.0 broadcast 10.1.255.255 + inet6 fe80::223:aeff:fe8c:33b6 prefixlen 64 scopeid 0x20 + ether 00:23:ae:8c:33:b6 txqueuelen 1000 (Ethernet) + RX packets 4634814 bytes 4923336168 (4.5 GiB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 1736369 bytes 1125167322 (1.0 GiB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + device interrupt 21 memory 0xf7ae0000-f7b00000 + diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-rhel5.6 b/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-rhel5.6 new file mode 100644 index 0000000..d972105 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/linux-rhel5.6 @@ -0,0 +1,83 @@ +eth0 Link encap:Ethernet HWaddr 00:1E:68:2F:85:D8 + inet addr:10.202.0.31 Bcast:0.0.0.0 Mask:255.255.252.0 + inet6 addr: fe80::21e:68ff:fe2f:85d8/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:520759581 errors:0 dropped:0 overruns:0 frame:0 + TX packets:371725829 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:482963442899 (449.7 GiB) TX bytes:502934726512 (468.3 GiB) + +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope:Host + UP LOOPBACK RUNNING MTU:16436 Metric:1 + RX packets:233 errors:0 dropped:0 overruns:0 frame:0 + TX packets:233 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:76262 (74.4 KiB) TX bytes:76262 (74.4 KiB) + +peth0 Link encap:Ethernet HWaddr 00:1E:68:2F:85:D8 + inet6 addr: fe80::21e:68ff:fe2f:85d8/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:584881096 errors:0 dropped:0 overruns:0 frame:0 + TX packets:617923109 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:509225387599 (474.2 GiB) TX bytes:559753550508 (521.3 GiB) + Interrupt:22 Memory:bb2e0000-bb300000 + +vif1.0 Link encap:Ethernet HWaddr FE:FF:FF:FF:FF:FF + inet6 addr: fe80::fcff:ffff:feff:ffff/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:67585 errors:0 dropped:0 overruns:0 frame:0 + TX packets:15622854 errors:0 dropped:11032 overruns:0 carrier:0 + collisions:0 txqueuelen:500 + RX bytes:17005436 (16.2 MiB) TX bytes:10596989626 (9.8 GiB) + +vif2.0 Link encap:Ethernet HWaddr FE:FF:FF:FF:FF:FF + inet6 addr: fe80::fcff:ffff:feff:ffff/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:71648111 errors:0 dropped:0 overruns:0 frame:0 + TX packets:97845281 errors:0 dropped:53743 overruns:0 carrier:0 + collisions:0 txqueuelen:500 + RX bytes:43701804161 (40.7 GiB) TX bytes:38327390497 (35.6 GiB) + +vif3.0 Link encap:Ethernet HWaddr FE:FF:FF:FF:FF:FF + inet6 addr: fe80::fcff:ffff:feff:ffff/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:58637080 errors:0 dropped:0 overruns:0 frame:0 + TX packets:68750122 errors:0 dropped:87822 overruns:0 carrier:0 + collisions:0 txqueuelen:500 + RX bytes:23043121399 (21.4 GiB) TX bytes:17021829879 (15.8 GiB) + +vif4.0 Link encap:Ethernet HWaddr FE:FF:FF:FF:FF:FF + inet6 addr: fe80::fcff:ffff:feff:ffff/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:11335966 errors:0 dropped:0 overruns:0 frame:0 + TX packets:26137002 errors:0 dropped:57719 overruns:0 carrier:0 + collisions:0 txqueuelen:500 + RX bytes:1957130634 (1.8 GiB) TX bytes:11553991733 (10.7 GiB) + +vif5.0 Link encap:Ethernet HWaddr FE:FF:FF:FF:FF:FF + inet6 addr: fe80::fcff:ffff:feff:ffff/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:20386 errors:0 dropped:0 overruns:0 frame:0 + TX packets:14745953 errors:0 dropped:56619 overruns:0 carrier:0 + collisions:0 txqueuelen:500 + RX bytes:2967075 (2.8 MiB) TX bytes:10055298002 (9.3 GiB) + +vif6.0 Link encap:Ethernet HWaddr FE:FF:FF:FF:FF:FF + inet6 addr: fe80::fcff:ffff:feff:ffff/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:163951 errors:0 dropped:0 overruns:0 frame:0 + TX packets:14866837 errors:0 dropped:76941 overruns:0 carrier:0 + collisions:0 txqueuelen:500 + RX bytes:157699881 (150.3 MiB) TX bytes:10051414310 (9.3 GiB) + +virbr0 Link encap:Ethernet HWaddr 00:00:00:00:00:00 + inet addr:192.168.122.1 Bcast:192.168.122.255 Mask:255.255.255.0 + inet6 addr: fe80::200:ff:fe00:0/64 Scope:Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:27 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:0 (0.0 b) TX bytes:6790 (6.6 KiB) diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/oi-2021.10 b/tests/samples/third_party/glpi_agent/generic/ifconfig/oi-2021.10 new file mode 100644 index 0000000..ab8b202 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/oi-2021.10 @@ -0,0 +1,10 @@ +lo0: flags=2001000849 mtu 8232 index 1 + inet 127.0.0.1 netmask ff000000 +e1000g0: flags=1004843 mtu 1500 index 2 + inet 192.168.1.97 netmask ffffff00 broadcast 192.168.1.255 +lo0: flags=2002000849 mtu 8252 index 1 + inet6 ::1/128 +e1000g0: flags=20002000841 mtu 1500 index 2 + inet6 fe80::a00:27ff:fefc:fb04/10 +e1000g0:1: flags=20002080841 mtu 1500 index 2 + inet6 2a01:e0a:35c:740:a00:27ff:fefc:fb04/64 diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/opensolaris b/tests/samples/third_party/glpi_agent/generic/ifconfig/opensolaris new file mode 100644 index 0000000..6c41924 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/opensolaris @@ -0,0 +1,12 @@ +lo0: flags=2001000849 mtu 8232 index 1 + inet 127.0.0.1 netmask ff000000 +e1000g0: flags=1004843 mtu 1500 index 2 + inet 192.168.0.41 netmask ffffff00 broadcast 192.168.0.255 + ether 8:0:27:fc:ad:56 +lo0: flags=2002000849 mtu 8252 index 1 + inet6 ::1/128 +e1000g0: flags=2000841 mtu 1480 index 2 + inet6 fe80::a00:27ff:fefc:ad56/10 + ether 8:0:27:fc:ad:56 +e1000g0:1: flags=2080841 mtu 1480 index 2 + inet6 2a01:e35:8ad7:48d0:a00:27ff:fefc:ad56/64 diff --git a/tests/samples/third_party/glpi_agent/generic/ifconfig/solaris-10 b/tests/samples/third_party/glpi_agent/generic/ifconfig/solaris-10 new file mode 100644 index 0000000..0d683d3 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/ifconfig/solaris-10 @@ -0,0 +1,20 @@ +lo0: flags=2001000849 mtu 8232 index 1 + inet 127.0.0.1 netmask ff000000 +aggr1: flags=1000843 mtu 1500 index 2 + inet 130.79.0.1 netmask fffffe00 broadcast 130.79.0.255 + ether 0:15:17:7a:60:31 +e1000g0: flags=1000843 mtu 1500 index 3 + inet 130.79.0.2 netmask ffffff80 broadcast 130.79.0.255 + ether 0:15:17:7a:60:30 +e1000g2: flags=1000843 mtu 1500 index 4 + inet 130.79.0.3 netmask ffffff80 broadcast 130.79.0.255 + ether 0:15:17:7a:60:32 +e1000g3: flags=1000843 mtu 1500 index 5 + inet 192.168.19.1 netmask ffffff00 broadcast 192.168.19.255 + ether 0:15:17:7a:60:33 +e1000g4: flags=1000843 mtu 1500 index 6 + inet 130.79.255.1 netmask ffffffe0 broadcast 130.79.255.255 + ether 0:15:17:8a:48:30 +igb0: flags=1000843 mtu 1500 index 7 + inet 192.168.20.1 netmask ffffff00 broadcast 192.168.20.255 + ether 0:15:17:6a:44:4c diff --git a/tests/samples/third_party/glpi_agent/generic/netstat/aix-5.3a b/tests/samples/third_party/glpi_agent/generic/netstat/aix-5.3a new file mode 100644 index 0000000..7fa2711 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/netstat/aix-5.3a @@ -0,0 +1,14 @@ +Tables de routage +Destination Gateway Flags Refs Use If Exp Groups + +Route tree for Protocol Family 2 (Internet): +default 192.168.2.254 UG 3 446237 en0 - - => +default 192.168.2.250 UG 7 596156 en0 - - +127/8 127.0.0.1 U 3 19077 lo0 - - +192.168.2.0 192.168.2.1 UHSb 0 0 en0 - - => +192.168.2/24 192.168.2.1 U 32 21793220 en0 - - +192.168.2.1 127.0.0.1 UGHS 25 44250 lo0 - - +192.168.2.255 192.168.2.1 UHSb 0 312 en0 - - + +Arborescence de la route pour la famille de protocoles 24 (Internet v6): +::1 ::1 UH 0 8 lo0 - - diff --git a/tests/samples/third_party/glpi_agent/generic/netstat/aix-5.3c b/tests/samples/third_party/glpi_agent/generic/netstat/aix-5.3c new file mode 100644 index 0000000..24b6736 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/netstat/aix-5.3c @@ -0,0 +1,27 @@ +Routing tables +Destination Gateway Flags Refs Use If Exp Groups + +Route tree for Protocol Family 2 (Internet): +default 10.3.0.253 UGS 1 0 en1 - - +10/8 10.3.0.254 UGS 17 1137090 en1 - - +10.3.40.0 10.3.40.160 UHSb 0 0 en1 - - => +10.3.40/24 10.3.40.160 U 2 1002538 en1 - - +10.3.40.160 127.0.0.1 UGHS 80 17315232 lo0 - - +10.3.40.255 10.3.40.160 UHSb 2 349 en1 - - +127/8 127.0.0.1 U 16 108743 lo0 - - +192.168.1.0 192.168.1.160 UHSb 0 0 en0 - - => +192.168.1/24 192.168.1.160 U 5 50617 en0 - - +192.168.1.160 127.0.0.1 UGHS 0 1 lo0 - - +192.168.1.255 192.168.1.160 UHSb 2 607 en0 - - +192.168.4.0 192.168.4.160 UHSb 0 0 en2 - - => +192.168.4/24 192.168.4.160 U 0 1 en2 - - +192.168.4.160 127.0.0.1 UGHS 0 1 lo0 - - +192.168.4.255 192.168.4.160 UHSb 3 534 en2 - - +192.168.201/24 192.168.203.254 UGS 0 0 en1 - - +192.168.203.0 192.168.203.160 UHSb 0 0 en1 - - => +192.168.203/24 192.168.203.160 U 1 0 en1 - - +192.168.203.160 127.0.0.1 UGHS 0 0 lo0 - - +192.168.203.255 192.168.203.160 UHSb 0 4 en1 - - + +Route tree for Protocol Family 24 (Internet v6): +::1 ::1 UH 6 15485 lo0 - - diff --git a/tests/samples/third_party/glpi_agent/generic/netstat/aix-6.1a b/tests/samples/third_party/glpi_agent/generic/netstat/aix-6.1a new file mode 100644 index 0000000..7eaabf4 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/netstat/aix-6.1a @@ -0,0 +1,19 @@ +Routing tables +Destination Gateway Flags Refs Use If Exp Groups + +Route Tree for Protocol Family 2 (Internet): +default 172.16.0.254 UG 0 7 en1 - - +10.0.0.0 10.0.0.103 UHSb 0 0 en0 - - => +10/21 10.0.0.103 U 0 5319 en0 - - +10.0.0.103 127.0.0.1 UGHS 0 3 lo0 - - +10.0.7.255 10.0.0.103 UHSb 0 4 en0 - - +39/16 172.16.0.21 UGS 0 5 en1 - - +127/8 127.0.0.1 U 9 220 lo0 - - +172.16.0.0 172.16.0.80 UHSb 0 0 en1 - - => +172.16/21 172.16.0.80 U 16 117544066 en1 - - +172.16.0.80 127.0.0.1 UGHS 18 76697322 lo0 - - +172.16.7.255 172.16.0.80 UHSb 0 4 en1 - - +192.0.0/24 172.16.0.254 UGS 0 0 en1 - - + +Route Tree for Protocol Family 24 (Internet v6): +::1 ::1 UH 0 174 lo0 - - diff --git a/tests/samples/third_party/glpi_agent/generic/netstat/aix-6.1b b/tests/samples/third_party/glpi_agent/generic/netstat/aix-6.1b new file mode 100644 index 0000000..c35ef7d --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/netstat/aix-6.1b @@ -0,0 +1,31 @@ +Routing tables +Destination Gateway Flags Refs Use If Exp Groups + +Route tree for Protocol Family 2 (Internet): +default 10.3.0.253 UG 0 523 en3 - - +10/8 10.3.0.254 UGS 0 16997 en3 - - +10.3.0.0 10.3.40.101 UHSb 0 0 en3 - - => +10.3/16 10.3.40.101 U 13 247478930 en3 - - +10.3.40.101 127.0.0.1 UGHS 0 99 lo0 - - +10.3.255.255 10.3.40.101 UHSb 0 75 en3 - - +127/8 127.0.0.1 U 18 5472 lo0 - - +192.168.1.0 192.168.1.1 UHSb 0 0 en2 - - => +192.168.1/24 192.168.1.1 U 4 336107712 en2 - - +192.168.1.1 127.0.0.1 UGHS 2 187 lo0 - - +192.168.1.255 192.168.1.1 UHSb 3 65221 en2 - - +192.168.3.0 192.168.3.1 UHSb 0 0 en0 - - => +192.168.3/24 192.168.3.1 U 2 532061473 en0 - - +192.168.3.1 127.0.0.1 UGHS 6 404 lo0 - - +192.168.3.255 192.168.3.1 UHSb 0 139 en0 - - +192.168.4.0 192.168.4.1 UHSb 0 0 en1 - - => +192.168.4/24 192.168.4.1 U 16 616023243 en1 - - +192.168.4.1 127.0.0.1 UGHS 2 246 lo0 - - +192.168.4.255 192.168.4.1 UHSb 0 122 en1 - - +192.168.201/24 192.168.203.254 UGS 0 3 en3 - - +192.168.203.0 192.168.203.101 UHSb 0 0 en3 - - => +192.168.203/24 192.168.203.101 U 0 0 en3 - - +192.168.203.101 127.0.0.1 UGHS 0 142 lo0 - - +192.168.203.255 192.168.203.101 UHSb 0 0 en3 - - + +Route tree for Protocol Family 24 (Internet v6): +::1%1 ::1%1 UH 0 124 lo0 - - diff --git a/tests/samples/third_party/glpi_agent/generic/netstat/hpux1 b/tests/samples/third_party/glpi_agent/generic/netstat/hpux1 new file mode 100644 index 0000000..0cc0761 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/netstat/hpux1 @@ -0,0 +1,9 @@ +Routing tables +Destination Gateway Flags Refs Interface Pmtu +127.0.0.1 127.0.0.1 UH 0 lo0 4136 +10.0.4.55 10.0.4.55 UH 0 lan0:1 4136 +10.0.4.56 10.0.4.56 UH 0 lan0 4136 +10.0.4.32 10.0.4.56 U 3 lan0 1500 +10.0.4.32 10.0.4.55 U 3 lan0:1 1500 +127.0.0.0 127.0.0.1 U 0 lo0 0 +default 10.0.4.33 UG 0 lan0 0 diff --git a/tests/samples/third_party/glpi_agent/generic/netstat/hpux2 b/tests/samples/third_party/glpi_agent/generic/netstat/hpux2 new file mode 100644 index 0000000..bba52b9 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/netstat/hpux2 @@ -0,0 +1,12 @@ +Routing tables +Destination Gateway Flags Refs Interface Pmtu +127.0.0.1 127.0.0.1 UH 0 lo0 4136 +10.0.0.48 10.0.0.48 UH 0 lan0 4136 +10.0.0.49 10.0.0.49 UH 0 lan0:2 4136 +192.168.210.40 192.168.210.40 UH 0 lan0:1 4136 +192.168.210.91 10.0.0.48 UH 0 lan0 0 +10.0.0.32 10.0.0.48 U 4 lan0 1500 +10.0.0.32 10.0.0.49 U 4 lan0:2 1500 +192.168.210.0 192.168.210.40 U 4 lan0:1 1500 +127.0.0.0 127.0.0.1 U 0 lo0 0 +default 10.0.0.33 UG 0 lan0 0 diff --git a/tests/samples/third_party/glpi_agent/generic/netstat/linux1 b/tests/samples/third_party/glpi_agent/generic/netstat/linux1 new file mode 100644 index 0000000..3c0e435 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/netstat/linux1 @@ -0,0 +1,5 @@ +Kernel IP routing table +Destination Gateway Genmask Flags MSS Window irtt Iface +192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 +192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 wlan0 +0.0.0.0 192.168.0.254 0.0.0.0 UG 0 0 0 eth0 diff --git a/tests/samples/third_party/glpi_agent/generic/netstat/macosx1 b/tests/samples/third_party/glpi_agent/generic/netstat/macosx1 new file mode 100644 index 0000000..659ba36 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/netstat/macosx1 @@ -0,0 +1,11 @@ +Routing tables + +Internet: +Destination Gateway Flags Refs Use Netif Expire +default 192.168.0.254 UGSc 10 2 en0 +127 127.0.0.1 UCS 0 0 lo0 +127.0.0.1 127.0.0.1 UH 10 8841 lo0 +169.254 link#4 UCS 0 0 en0 +192.168.0 link#4 UCS 1 0 en0 +192.168.0.27 127.0.0.1 UHS 0 13 lo0 +192.168.0.254 f4:ca:e5:42:38:37 UHLW 8 21 en0 1001 diff --git a/tests/samples/third_party/glpi_agent/generic/netstat/netbsd b/tests/samples/third_party/glpi_agent/generic/netstat/netbsd new file mode 100644 index 0000000..2cfba7c --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/netstat/netbsd @@ -0,0 +1,11 @@ +Routing tables + +Internet: +Destination Gateway Flags Refs Use Mtu Interface +default 10.0.1.1 UGS 0 28026 - wm0 +10.0.1/24 link#1 UC 2 0 - wm0 +10.0.1.1 00:1d:7e:43:96:57 UHLc 1 277 - wm0 +10.0.1.101 00:1e:c2:0c:36:27 UHLc 0 1 - wm0 +10.0.1.124 127.0.0.1 UGHS 0 0 33192 lo0 +127/8 127.0.0.1 UGRS 0 0 33192 lo0 +127.0.0.1 127.0.0.1 UH 2 185 33192 lo0 diff --git a/tests/samples/third_party/glpi_agent/generic/netstat/openbsd b/tests/samples/third_party/glpi_agent/generic/netstat/openbsd new file mode 100644 index 0000000..53b33b8 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/generic/netstat/openbsd @@ -0,0 +1,12 @@ +Routing tables + +Internet: +Destination Gateway Flags Refs Use Mtu Prio Iface +default 10.0.1.1 UGS 0 0 - 8 em0 +10.0.1/24 link#1 UC 2 0 - 4 em0 +10.0.1.1 00:1d:7e:43:96:57 UHLc 1 3 - 4 em0 +10.0.1.100 00:13:77:b3:cf:0c UHLc 1 183 - 4 em0 +10.0.1.115 127.0.0.1 UGHS 0 0 33200 8 lo0 +127/8 127.0.0.1 UGRS 0 0 33200 8 lo0 +127.0.0.1 127.0.0.1 UH 2 0 33200 4 lo0 +224/4 127.0.0.1 URS 0 0 33200 8 lo0 diff --git a/tests/samples/third_party/glpi_agent/hpux/README b/tests/samples/third_party/glpi_agent/hpux/README new file mode 100644 index 0000000..4f66697 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/README @@ -0,0 +1,60 @@ +HPUX specific command and file samples + +Data origin: +- hpux1, hpux2 samples: Amir Pakdel +- hpux3, hpux4 samples: Earl Flack + +bdf: +- hpux*-X: bdf -t X + +CIMUtil: CIMUtil command +- hpux*: CIMUtil -e root/cimv2 HP_ManagementProcessor + +cprop: crop command +- hpux*-cpu: cprop -summary -c Processors +- hpux*-memory: cprop -summary -c Memory + +cstm: cstm command +- hpux*-MEMORY: echo 'sc product IPF_MEMORY;il' | /usr/sbin/cstm +- hpux*-mem: echo 'sc product mem;il' | /usr/sbin/cstm +- hpux*-cpu: echo 'sc product cpu;il' | /usr/sbin/cstm +- hpux*-system: echo 'sc product system;il' | /usr/sbin/cstm + +fstyp: fstyp command +- hpux*: fstyp -l + +getMPInfo.cgi: getMPInfo.cgi command +- hpux*: getMPInfo.cgi + +hpvminfo: hpvminfo command +- hpux*: hpvminfo + +ioscan: ioscan command +- hpux*-tape: ioscan -kFnC tape +- hpux*-disk: ioscan -kFnC disk +- hpux*-processor: ioscan -kFC processor +- hpux*-ioa: ioscan -kFC io +- hpux*-ba: ioscan -kFC ba +- hpux*-ext_bus: ioscan -kFC ext_bus +- hpux*-fs: ioscan -kFC fc + +lanscan: lanscan command +- hpux*: lanscan -iap + +lanadmin: lanadmin command +- hpux*-X: lanscan -g X + +machinfo: machinfo command +- hpux*: machinfo + +model: model command +- hpux*: model + +netstat: netstat command +- hpux*: netstat -nrv + +swapinfo: swapinfo command +- hpux*: swapinfo -dt + +swlist: swlist command +- hpux*: swlist diff --git a/tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux1-0 b/tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux1-0 new file mode 100644 index 0000000..00005dd --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux1-0 @@ -0,0 +1,41 @@ + + LAN INTERFACE STATUS DISPLAY + Mon, May 23,2011 12:01:08 + +PPA Number = 0 +Description = lan0 HP PCI-X 1000Base-T Release PHNE_36236 B.11.23.0706.02 +Type (value) = ethernet-csmacd(6) +MTU Size = 1500 +Speed = 1000000000 +Station Address = 0x16353eac5c +Administration Status (value) = up(1) +Operation Status (value) = up(1) +Last Change = 287 +Inbound Octets = 3964472983 +Inbound Unicast Packets = 901741518 +Inbound Non-Unicast Packets = 18997 +Inbound Discards = 0 +Inbound Errors = 0 +Inbound Unknown Protocols = 235 +Outbound Octets = 1880378407 +Outbound Unicast Packets = 507550720 +Outbound Non-Unicast Packets = 11245 +Outbound Discards = 0 +Outbound Errors = 0 +Outbound Queue Length = 0 +Specific = 655367 + +Ethernet-like Statistics Group + +Index = 1 +Alignment Errors = 0 +FCS Errors = 0 +Single Collision Frames = 0 +Multiple Collision Frames = 0 +Deferred Transmissions = 0 +Late Collisions = 0 +Excessive Collisions = 0 +Internal MAC Transmit Errors = 0 +Carrier Sense Errors = 0 +Frames Too Long = 0 +Internal MAC Receive Errors = 0 diff --git a/tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux1-1 b/tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux1-1 new file mode 100644 index 0000000..9bd9dab --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux1-1 @@ -0,0 +1,41 @@ + + LAN INTERFACE STATUS DISPLAY + Mon, May 23,2011 12:01:10 + +PPA Number = 1 +Description = lan1 HP PCI-X 1000Base-T Release PHNE_36236 B.11.23.0706.02 +Type (value) = ethernet-csmacd(6) +MTU Size = 1500 +Speed = 1000000000 +Station Address = 0x16353eac5d +Administration Status (value) = up(1) +Operation Status (value) = up(1) +Last Change = 284 +Inbound Octets = 2951500 +Inbound Unicast Packets = 0 +Inbound Non-Unicast Packets = 30242 +Inbound Discards = 0 +Inbound Errors = 0 +Inbound Unknown Protocols = 30242 +Outbound Octets = 0 +Outbound Unicast Packets = 0 +Outbound Non-Unicast Packets = 0 +Outbound Discards = 0 +Outbound Errors = 0 +Outbound Queue Length = 0 +Specific = 655367 + +Ethernet-like Statistics Group + +Index = 2 +Alignment Errors = 0 +FCS Errors = 0 +Single Collision Frames = 0 +Multiple Collision Frames = 0 +Deferred Transmissions = 0 +Late Collisions = 0 +Excessive Collisions = 0 +Internal MAC Transmit Errors = 0 +Carrier Sense Errors = 0 +Frames Too Long = 0 +Internal MAC Receive Errors = 0 diff --git a/tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux2-0 b/tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux2-0 new file mode 100644 index 0000000..e104e5f --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux2-0 @@ -0,0 +1,41 @@ + + LAN INTERFACE STATUS DISPLAY + Mon, May 23,2011 11:42:01 + +PPA Number = 0 +Description = lan0 HP PCI-X 1000Base-T Release PHNE_36236 B.11.23.0706.02 +Type (value) = ethernet-csmacd(6) +MTU Size = 1500 +Speed = 1000000000 +Station Address = 0x18fe28e080 +Administration Status (value) = up(1) +Operation Status (value) = up(1) +Last Change = 268 +Inbound Octets = 1555284142 +Inbound Unicast Packets = 1565864523 +Inbound Non-Unicast Packets = 1 +Inbound Discards = 0 +Inbound Errors = 0 +Inbound Unknown Protocols = 55 +Outbound Octets = 3382475092 +Outbound Unicast Packets = 630798380 +Outbound Non-Unicast Packets = 40950 +Outbound Discards = 0 +Outbound Errors = 0 +Outbound Queue Length = 0 +Specific = 655367 + +Ethernet-like Statistics Group + +Index = 1 +Alignment Errors = 0 +FCS Errors = 0 +Single Collision Frames = 0 +Multiple Collision Frames = 0 +Deferred Transmissions = 0 +Late Collisions = 0 +Excessive Collisions = 0 +Internal MAC Transmit Errors = 0 +Carrier Sense Errors = 0 +Frames Too Long = 0 +Internal MAC Receive Errors = 0 diff --git a/tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux2-1 b/tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux2-1 new file mode 100644 index 0000000..67a2bfb --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/lanadmin/hpux2-1 @@ -0,0 +1,41 @@ + + LAN INTERFACE STATUS DISPLAY + Mon, May 23,2011 11:42:04 + +PPA Number = 1 +Description = lan1 HP PCI-X 1000Base-T Release PHNE_36236 B.11.23.0706.02 +Type (value) = ethernet-csmacd(6) +MTU Size = 1500 +Speed = 1000000000 +Station Address = 0x18fe28e081 +Administration Status (value) = up(1) +Operation Status (value) = up(1) +Last Change = 283 +Inbound Octets = 2620864 +Inbound Unicast Packets = 0 +Inbound Non-Unicast Packets = 40951 +Inbound Discards = 0 +Inbound Errors = 0 +Inbound Unknown Protocols = 40951 +Outbound Octets = 0 +Outbound Unicast Packets = 0 +Outbound Non-Unicast Packets = 0 +Outbound Discards = 0 +Outbound Errors = 0 +Outbound Queue Length = 0 +Specific = 655367 + +Ethernet-like Statistics Group + +Index = 2 +Alignment Errors = 0 +FCS Errors = 0 +Single Collision Frames = 0 +Multiple Collision Frames = 0 +Deferred Transmissions = 0 +Late Collisions = 0 +Excessive Collisions = 0 +Internal MAC Transmit Errors = 0 +Carrier Sense Errors = 0 +Frames Too Long = 0 +Internal MAC Receive Errors = 0 diff --git a/tests/samples/third_party/glpi_agent/hpux/lanscan/hpux b/tests/samples/third_party/glpi_agent/hpux/lanscan/hpux new file mode 100644 index 0000000..675e71e --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/lanscan/hpux @@ -0,0 +1,42 @@ +0x0017A47708A4 lan1 snap1 1 +0x0017A47708E8 lan10 snap10 10 +0x0017A47708EC lan11 snap11 11 +0x1CC1DE40C6BE lan12 snap12 12 +0x1CC1DE40C6C2 lan13 snap13 13 +0x1CC1DE40C6BF lan14 snap14 14 +0x1CC1DE40C6C3 lan15 snap15 15 +0x0017A47708A8 lan2 snap2 2 +0x0017A47708AC lan3 snap3 3 +0x0017A47708F0 lan16 snap16 16 +0x0017A47708F4 lan17 snap17 17 +0x1CC1DE40C6C6 lan18 snap18 18 +0x1CC1DE40C6CA lan19 snap19 19 +0x1CC1DE40C6C7 lan20 snap20 20 +0x1CC1DE40C6CB lan21 snap21 21 +0x0017A47708F8 lan22 snap22 22 +0x0017A47708FC lan37 snap37 37 +0xD8D385D81462 lan23 snap23 23 +0xD8D385D81466 lan38 snap38 38 +0xD8D385D81463 lan24 snap24 24 +0xD8D385D81467 lan39 snap39 39 +0x0017A47708A2 lan6 snap6 6 +0x0017A47708A6 lan7 snap7 7 +0x0017A47708B2 lan25 snap25 25 +0x0017A47708E6 lan26 snap26 26 +0x0017A47708F2 lan27 snap27 27 +0x0017A47708F6 lan28 snap28 28 +0x1CC1DE40E64B lan29 snap29 29 +0x1CC1DE40E64F lan30 snap30 30 +0x0017A47708AA lan8 snap8 8 +0x0017A47708AE lan9 snap9 9 +0x0017A47708EA lan31 snap31 31 +0x0017A47708EE lan32 snap32 32 +0x0017A47708FA lan33 snap33 33 +0x0017A47708FE lan34 snap34 34 +0x1CC1DE40E653 lan35 snap35 35 +0x1CC1DE40E657 lan36 snap36 36 +0x0017A47708A0 lan900 snap900 900 +0x000000000000 lan901 snap901 901 +0x0017A47708B0 lan902 snap902 902 +0x000000000000 lan903 snap903 903 +0x000000000000 lan904 snap904 904 diff --git a/tests/samples/third_party/glpi_agent/hpux/lanscan/hpux1 b/tests/samples/third_party/glpi_agent/hpux/lanscan/hpux1 new file mode 100644 index 0000000..b07d8c7 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/lanscan/hpux1 @@ -0,0 +1,2 @@ +0x0016353EAC5C lan0 snap0 0 +0x0016353EAC5D lan1 snap1 1 diff --git a/tests/samples/third_party/glpi_agent/hpux/lanscan/hpux2 b/tests/samples/third_party/glpi_agent/hpux/lanscan/hpux2 new file mode 100644 index 0000000..c63df1b --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/lanscan/hpux2 @@ -0,0 +1,7 @@ +0x0016353EAC44 lan0 snap0 0 +0x0016353EAC45 lan1 snap1 1 +0x000000000000 lan900 snap900 900 +0x000000000000 lan901 snap901 901 +0x000000000000 lan902 snap902 902 +0x000000000000 lan903 snap903 903 +0x000000000000 lan904 snap904 904 diff --git a/tests/samples/third_party/glpi_agent/hpux/netstat/hpux b/tests/samples/third_party/glpi_agent/hpux/netstat/hpux new file mode 100644 index 0000000..0f08f64 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/netstat/hpux @@ -0,0 +1,7 @@ +Routing tables +Dest/Netmask Gateway Flags Refs Interface Pmtu +127.0.0.1/255.255.255.255 127.0.0.1 UH 0 lo0 32808 +172.24.70.121/255.255.255.255 172.24.70.121 UH 0 lan0 32808 +172.24.70.0/255.255.255.0 172.24.70.121 U 2 lan0 1500 +127.0.0.0/255.0.0.0 127.0.0.1 U 0 lo0 32808 +default/0.0.0.0 172.24.70.254 UG 0 lan0 1500 diff --git a/tests/samples/third_party/glpi_agent/hpux/netstat/hpux1 b/tests/samples/third_party/glpi_agent/hpux/netstat/hpux1 new file mode 100644 index 0000000..e08a3da --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/netstat/hpux1 @@ -0,0 +1,9 @@ +Routing tables +Dest/Netmask Gateway Flags Refs Interface Pmtu +127.0.0.1/255.255.255.255 127.0.0.1 UH 0 lo0 4136 +10.0.4.55/255.255.255.255 10.0.4.55 UH 0 lan0:1 4136 +10.0.4.56/255.255.255.255 10.0.4.56 UH 0 lan0 4136 +10.0.4.32/255.255.255.224 10.0.4.56 U 3 lan0 1500 +10.0.4.32/255.255.255.224 10.0.4.55 U 3 lan0:1 1500 +127.0.0.0/255.0.0.0 127.0.0.1 U 0 lo0 0 +default/0.0.0.0 10.0.4.33 UG 0 lan0 0 diff --git a/tests/samples/third_party/glpi_agent/hpux/netstat/hpux2 b/tests/samples/third_party/glpi_agent/hpux/netstat/hpux2 new file mode 100644 index 0000000..8d243a1 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/netstat/hpux2 @@ -0,0 +1,7 @@ +Routing tables +Dest/Netmask Gateway Flags Refs Interface Pmtu +127.0.0.1/255.255.255.255 127.0.0.1 UH 0 lo0 4136 +10.0.3.60/255.255.255.255 10.0.3.60 UH 0 lan0 4136 +10.0.3.0/255.255.255.0 10.0.3.60 U 2 lan0 1500 +127.0.0.0/255.0.0.0 127.0.0.1 U 0 lo0 4136 +default/0.0.0.0 10.0.3.1 UG 0 lan0 1500 diff --git a/tests/samples/third_party/glpi_agent/hpux/netstat/hpux3 b/tests/samples/third_party/glpi_agent/hpux/netstat/hpux3 new file mode 100644 index 0000000..2eb69fe --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/netstat/hpux3 @@ -0,0 +1,11 @@ +Routing tables +Dest/Netmask Gateway Flags Refs Interface Pmtu +127.0.0.1/255.255.255.255 127.0.0.1 UH 0 lo0 32808 +10.46.236.71/255.255.255.255 10.46.236.71 UH 0 lan5000 32808 +10.46.228.71/255.255.255.255 10.46.228.71 UH 0 lan5001 32808 +10.46.236.121/255.255.255.255 10.46.236.121 UH 0 lan5000:801 32808 +10.46.236.0/255.255.255.0 10.46.236.71 U 3 lan5000 1500 +10.46.236.0/255.255.255.0 10.46.236.121 U 3 lan5000:801 1500 +10.46.228.0/255.255.255.0 10.46.228.71 U 2 lan5001 1500 +127.0.0.0/255.0.0.0 127.0.0.1 U 0 lo0 32808 +default/0.0.0.0 10.46.236.252 UG 0 lan5000 1500 diff --git a/tests/samples/third_party/glpi_agent/hpux/nwmgr/sample1 b/tests/samples/third_party/glpi_agent/hpux/nwmgr/sample1 new file mode 100644 index 0000000..8e8fdf5 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/nwmgr/sample1 @@ -0,0 +1,27 @@ + +Name/ Interface Station Sub- Interface Related +ClassInstance State Address system Type Interface +============== ========= ============== ======== ============== ========= +lan0 UP 0x0017A4770428 iexgbe 10GBASE-KR lan900 +lan1 UP 0x0017A477042A iexgbe 10GBASE-KR +lan2 DOWN 0x0017A4770438 iexgbe 10GBASE-KR +lan3 DOWN 0x0017A477043A iexgbe 10GBASE-KR +lan4 DOWN 0x984BE15BD082 iexgbe 10GBASE-KR +lan5 DOWN 0x984BE15BD086 iexgbe 10GBASE-KR +lan6 DOWN 0x984BE15BD083 iexgbe 10GBASE-KR +lan7 DOWN 0x984BE15BD087 iexgbe 10GBASE-KR +lan8 UP 0x0017A477042C iexgbe 10GBASE-KR +lan9 UP 0x0017A477042E iexgbe 10GBASE-KR +lan10 UP 0x0017A4770446 iexgbe 10GBASE-KR lan901 +lan11 UP 0x0017A4770448 iexgbe 10GBASE-KR lan901 +lan12 DOWN 0x984BE15BD08A iexgbe 10GBASE-KR +lan13 DOWN 0x984BE15BD08E iexgbe 10GBASE-KR +lan14 DOWN 0x984BE15BD08B iexgbe 10GBASE-KR +lan15 DOWN 0x984BE15BD08F iexgbe 10GBASE-KR +lan900 UP 0x0017A4770428 hp_apa hp_apa +lan5000 UP 0x0017A4770428 vlan vlan lan900 +lan901 UP 0x0017A4770446 hp_apa hp_apa +lan5001 UP 0x0017A4770446 vlan vlan lan901 +lan902 DOWN 0x000000000000 hp_apa hp_apa +lan903 DOWN 0x000000000000 hp_apa hp_apa +lan904 DOWN 0x000000000000 hp_apa hp_apa diff --git a/tests/samples/third_party/glpi_agent/hpux/nwmgr/sample2 b/tests/samples/third_party/glpi_agent/hpux/nwmgr/sample2 new file mode 100644 index 0000000..61af831 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/hpux/nwmgr/sample2 @@ -0,0 +1,27 @@ + +Name/ Interface Station Sub- Interface Related +ClassInstance State Address system Type Interface +============== ========= ============== ======== ============== ========= +lan0 UP 0x0017A4770020 iexgbe 10GBASE-KR lan900 +lan1 UP 0x0017A4770022 iexgbe 10GBASE-KR lan900 +lan2 DOWN 0x0017A4770042 iexgbe 10GBASE-KR +lan3 DOWN 0x0017A477003E iexgbe 10GBASE-KR +lan4 DOWN 0x984BE15B7328 iexgbe 10GBASE-KR +lan5 DOWN 0x984BE15B732C iexgbe 10GBASE-KR +lan6 DOWN 0x984BE15B7329 iexgbe 10GBASE-KR +lan7 DOWN 0x984BE15B732D iexgbe 10GBASE-KR +lan8 UP 0x0017A4770024 iexgbe 10GBASE-KR +lan9 UP 0x0017A4770026 iexgbe 10GBASE-KR +lan10 UP 0x0017A4770040 iexgbe 10GBASE-KR lan901 +lan11 UP 0x0017A4770050 iexgbe 10GBASE-KR lan901 +lan12 DOWN 0x984BE15B7330 iexgbe 10GBASE-KR +lan13 DOWN 0x984BE15B7334 iexgbe 10GBASE-KR +lan14 DOWN 0x984BE15B7331 iexgbe 10GBASE-KR +lan15 DOWN 0x984BE15B7335 iexgbe 10GBASE-KR +lan900 UP 0x0017A4770020 hp_apa hp_apa +lan5000 UP 0x0017A4770020 vlan vlan lan900 +lan901 UP 0x0017A4770040 hp_apa hp_apa +lan5001 UP 0x0017A4770040 vlan vlan lan901 +lan902 DOWN 0x000000000000 hp_apa hp_apa +lan903 DOWN 0x000000000000 hp_apa hp_apa +lan904 DOWN 0x000000000000 hp_apa hp_apa diff --git a/tests/samples/third_party/glpi_agent/linux/README b/tests/samples/third_party/glpi_agent/linux/README new file mode 100644 index 0000000..c52a088 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/linux/README @@ -0,0 +1,30 @@ +linux specific command output and files + +blkid: blkid command output + +ddcprobe: ddcprobe command output + +equery: equery command output +- *: equery -V + +hal: hal command output + +ip_addr: ip command output + +mpt-status: mpt-status command output + +packaging: packaging command output + +proc/cpuinfo: /proc/cpuinfo file + +proc/scsi: /proc/scsi file + +release: /etc/release file + +rhn-systemid: /etc/sysconfig/rhn/systemid file + +smartctl: smartctl command output + +tw_cli: tw_cli command output + +udev: /dev/.udev file diff --git a/tests/samples/third_party/glpi_agent/linux/ip/default-gateway-1 b/tests/samples/third_party/glpi_agent/linux/ip/default-gateway-1 new file mode 100644 index 0000000..46c96d4 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/linux/ip/default-gateway-1 @@ -0,0 +1 @@ +default via 192.168.1.254 dev wlan0 proto dhcp src 192.168.1.10 metric 600 diff --git a/tests/samples/third_party/glpi_agent/linux/ip/default-gateway-2 b/tests/samples/third_party/glpi_agent/linux/ip/default-gateway-2 new file mode 100644 index 0000000..3df7291 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/linux/ip/default-gateway-2 @@ -0,0 +1,2 @@ +default via 192.168.1.254 dev eth0 proto dhcp src 192.168.1.11 metric 100 +default via 192.168.2.254 dev lxcbr proto static metric 425 diff --git a/tests/samples/third_party/glpi_agent/linux/ip/default-gateway-3 b/tests/samples/third_party/glpi_agent/linux/ip/default-gateway-3 new file mode 100644 index 0000000..d3a33e1 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/linux/ip/default-gateway-3 @@ -0,0 +1,2 @@ +default via 192.168.1.254 dev ens193 onlink + diff --git a/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-1 b/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-1 new file mode 100644 index 0000000..c13ce08 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-1 @@ -0,0 +1,25 @@ +1: lo: mtu 16436 qdisc noqueue state UNKNOWN + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever +2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 + link/ether 00:23:18:91:db:8d brd ff:ff:ff:ff:ff:ff + inet 192.168.0.10/24 brd 192.168.0.255 scope global eth0 + inet6 2a01:e34:ef9c:64d0:223:18ff:fe91:db8d/64 scope global dynamic + valid_lft 85996sec preferred_lft 85996sec + inet6 fe80::223:18ff:fe91:db8d/64 scope link + valid_lft forever preferred_lft forever +4: tun0: mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 100 + link/none + inet 192.168.41.6 peer 192.168.41.5/32 scope global tun0 +6: tun1: mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 100 + link/none + inet 192.168.222.14 peer 192.168.222.13/32 scope global tun1 +23: wlan0: mtu 1500 qdisc mq state DOWN qlen 1000 + link/ether e8:39:df:3f:7d:ef brd ff:ff:ff:ff:ff:ff +24: tap0: mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 100 + link/ether 8a:84:99:29:46:05 brd ff:ff:ff:ff:ff:ff + inet 172.28.218.101/24 brd 172.28.218.255 scope global tap0 + inet6 fe80::8884:99ff:fe29:4605/64 scope link + valid_lft forever preferred_lft forever diff --git a/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-2 b/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-2 new file mode 100644 index 0000000..be3d4ba --- /dev/null +++ b/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-2 @@ -0,0 +1,17 @@ +1: lo: mtu 16436 qdisc noqueue state UNKNOWN + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever +2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 + link/ether 0f:0f:0f:0f:0f:0f brd ff:ff:ff:ff:ff:ff + inet 11.11.11.11/25 brd 11.11.11.127 scope global eth0 + inet 172.16.0.201/17 brd 172.16.127.255 scope global eth0 + inet6 fe80::201:29ff:fed1:feb4/64 scope link + valid_lft forever preferred_lft forever +3: eql: mtu 576 qdisc noop state DOWN qlen 5 + link/slip +4: sit0: mtu 1480 qdisc noop state DOWN + link/sit 0.0.0.0 brd 0.0.0.0 +5: wlan0: mtu 1500 qdisc mq state DOWN qlen 1000 + link/ether 0f:0f:0f:0f:0f:0f brd ff:ff:ff:ff:ff:ff diff --git a/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-3 b/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-3 new file mode 100644 index 0000000..fc0f8c2 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-3 @@ -0,0 +1,12 @@ +1: lo: mtu 16436 qdisc noqueue + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever +2: eth0: mtu 1500 qdisc pfifo_fast qlen 1000 + link/ether 00:50:56:8b:70:b7 brd ff:ff:ff:ff:ff:ff + inet 10.40.1.16/24 brd 10.40.1.255 scope global eth0 + inet6 fe80::250:56ff:fe8b:70b7/64 scope link + valid_lft forever preferred_lft forever +3: sit0: mtu 1480 qdisc noop + link/sit 0.0.0.0 brd 0.0.0.0 diff --git a/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-4 b/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-4 new file mode 100644 index 0000000..8c006df --- /dev/null +++ b/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-4 @@ -0,0 +1,3 @@ +14: ib0: mtu 2044 qdisc mq state UP qlen 1024 + link/infiniband a0:00:01:00:fe:80:00:00:00:00:00:00:00:02:c9:03:00:15:94:d1 brd 00:ff:ff:ff:ff:12:40:1b:ff:ff:00:00:00:00:00:00:ff:ff:ff:ff + inet 10.89.96.11/20 scope global ib0 diff --git a/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-5 b/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-5 new file mode 100644 index 0000000..755c90c --- /dev/null +++ b/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-5 @@ -0,0 +1,22 @@ +1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever +2: wlan0: mtu 1500 qdisc mq state UP group default qlen 1000 + link/ether 00:1a:6b:0d:78:03 brd ff:ff:ff:ff:ff:ff + inet 128.93.189.62/22 brd 128.93.191.255 scope global dynamic wlan0 + valid_lft 14226sec preferred_lft 14226sec + inet 192.168.1.11/24 brd 192.168.1.255 scope global wlan0:0 + valid_lft forever preferred_lft forever + inet6 fe80::21a:6bff:fe0d:7803/64 scope link + valid_lft forever preferred_lft forever +3: eth0: mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000 + link/ether f0:1f:af:0f:a4:0c brd ff:ff:ff:ff:ff:ff + inet 192.168.1.11/24 brd 192.168.1.255 scope global eth0:0 + valid_lft forever preferred_lft forever +4: bond0: mtu 1500 qdisc noqueue state DOWN group default + link/ether 8e:b2:64:1a:0b:00 brd ff:ff:ff:ff:ff:ff +5: br0: mtu 1500 qdisc noqueue state UNKNOWN group default + link/ether aa:4b:c2:02:31:15 brd ff:ff:ff:ff:ff:ff diff --git a/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-el8 b/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-el8 new file mode 100644 index 0000000..2526810 --- /dev/null +++ b/tests/samples/third_party/glpi_agent/linux/ip/ip_addr-el8 @@ -0,0 +1,26 @@ +1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever +2: eth0: mtu 1500 qdisc mq state UP group default qlen 1000 + link/ether e4:11:5b:ed:36:0c brd ff:ff:ff:ff:ff:ff + inet 10.10.220.100/32 scope global eth0:srv + valid_lft forever preferred_lft forever +3: eth2: mtu 1500 qdisc noop state DOWN group default qlen 1000 + link/ether e4:11:5b:ed:36:0e brd ff:ff:ff:ff:ff:ff +4: eth1: mtu 1500 qdisc noop state DOWN group default qlen 1000 + link/ether e4:11:5b:ed:36:38 brd ff:ff:ff:ff:ff:ff +5: eth3: mtu 1500 qdisc noop state DOWN group default qlen 1000 + link/ether e4:11:5b:ed:36:3a brd ff:ff:ff:ff:ff:ff +6: macvlan0@eth0: mtu 1500 qdisc noqueue state UP group default qlen 1000 + link/ether 4e:05:62:03:69:e7 brd ff:ff:ff:ff:ff:ff + inet 10.10.220.101/24 brd 10.10.220.255 scope global dynamic noprefixroute macvlan0 + valid_lft 76222sec preferred_lft 76222sec + inet6 fe80::260f:e9bb:60de:58f4/64 scope link noprefixroute + valid_lft forever preferred_lft forever +7: docker0: mtu 1500 qdisc noqueue state DOWN group default + link/ether 02:42:0c:d5:0f:d7 brd ff:ff:ff:ff:ff:ff + inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 + valid_lft forever preferred_lft forever From 9d0b0aeacf7e57ca25473b3b4f4e4a043e301564 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 23:17:33 -0700 Subject: [PATCH 102/112] feat: add LanscanIface method --- CHANGELOG.md | 1 + README.md | 6 ++++-- docs/misc_docs/TODO.md | 3 ++- getmac/getmac.py | 35 ++++++++++++++++++++++++++++++++++- pyproject.toml | 2 +- tests/test_methods.py | 28 ++++++++++++++++++++++++++++ 6 files changed, 70 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcdbc10..f2fa155 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Handle `bytes` values for `interface`, `ip`, `ip6`, and `hostname` arguments to `get_mac_address()`. This will just call `decode("utf-8")` on the bytes. This is to ease usage with certain libraries and functions that return strings as bytes. - If you pass it some weird bytes that aren't decodable as utf-8, it's obviously going to break. Don't be dumb :) * Added manpage for the command line interface (`getmac.1`) +* New method, `LanscanIface`, a proper implementation of `lanscan -ia` for HP-UX systems. This replaces the functionality of the removed `UuidLanscan` method, and is loosely based on CPython's `uuid._lanscan_getnode()` implementation. ### Changed * BREAKING CHANGE: refactored how settings are handled. Instead of module-level globals, they're implemented in a Settings singleton in `getmac.settings`. For example, `getmac.getmac.PORT` should now be `getmac.settings.PORT`. diff --git a/README.md b/README.md index 086130b..b2bc6c3 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,7 @@ Contributors are more than welcome! See the [contribution guide](CONTRIBUTING.md Before submitting a PR, please make sure you've completed the [pull request checklist](CONTRIBUTING.md#Code_requirements)! -The [Python Discord server](https://discord.gg/python) is a good place to ask questions or discuss the project (Handle: @KnownError#0001). +The [Python Discord server](https://discord.gg/python) is a good place to ask questions or discuss the project (handle: `@knownerror`). ## Contributors - Christopher Goes (@ghostofgoes) - Author and maintainer @@ -283,6 +283,8 @@ Many of the methods used to acquire an address and the core logic framework are - [_unix_fcntl_by_interface](https://stackoverflow.com/a/4789267/2214380) - [_windows_get_remote_mac_ctypes](goo.gl/ymhZ9p) - [String joining](https://stackoverflow.com/a/3258612/2214380) +- [Facter](https://github.com/puppetlabs/facter) - Command output examples from Facter are leveraged for better test coverage +- [GLPI Agent](https://github.com/glpi-project/glpi-agent) - Command output examples from GLPI Agent are leveraged for better test coverage ## License -MIT. Feel free to copy, modify, and use to your heart's content. Enjoy :) +The code is licensed MIT. The test data contains Apache 2.0 and GPLv2 licensed files, in the `tests/samples/third_party/` directory. diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index 7c4d2bb..126b36a 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -46,6 +46,7 @@ ## Enhancements/fixes/misc. - [x] Python 3.13 + 3.14 +- [ ] Fix `UuidLanscan` for Python 3.9+ - [ ] [issue #76](https://github.com/GhostofGoes/getmac/issues/76): get_mac_address() is caching an old mac address, no longer present in local ARP - get_mac_address() is caching an old mac address for a given IP, even when it has timeout from OS ARP table. Only an explicit delete of the ARP entry on the OS make it return '00:00:00:00:00:00' again. - Fix is to check that the flag != 0x0, which should do the trick, unless there's an edge case that it misses. @@ -144,11 +145,11 @@ This is going to be a bit more complicated since the highest metric routes are g - [ ] Profiling: CPU usage, memory usage, run time/load time ## Misc. +- [ ] HP-UX. Detect HP-UX. Default to `lan0` if default interface can't be found. Add `lanscan` and `nwmgr` support. - [ ] Add ability to match user-provided arguments case-insensitively - [ ] Add ability to get the mac address of a Python socket's interface (`socket.socket`) - [ ] Test against non-ethernet interfaces (WiFi, LTE, etc.) - [ ] Create a script to collect samples for all relevant commands on a platform and save output into the appropriately named sub-directory in `samples/`. -- [x] Use [ruff](https://github.com/charliermarsh/ruff) instead of `flake8`? FastAPI, Scikit learn, [PyPA build](https://github.com/pypa/build/pull/565/), and other major projects are moving to it # Documentation diff --git a/getmac/getmac.py b/getmac/getmac.py index 99ad050..656a5a7 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -483,6 +483,39 @@ def get(self, arg: str) -> Optional[str]: return None if data is not None and len(data) < 17 else data +class LanscanIface(Method): + """ + Uses the ``lanscan`` command to get the MAC address of a network interface. + + This is adopted from Python's :mod:`uuid` module's ``_lanscan_getnode`` function. + """ + + platforms = {"other"} + method_type = "iface" + + def test(self) -> bool: + return utils.check_command("lanscan") + + def get(self, arg: str) -> Optional[str]: + # -a: Display station addresses only. No headings. + # -i: Display interface names only. No headings. + output = utils.popen("lanscan", "-ai") + if not output: + return None + + # Find the line containing the interface name, e.g. "lan0" + search_for = arg + " " # space to prevent partial matches + for line in output.splitlines(): + if search_for in line: + # Extract MAC address from the line + # The raw MAC will be something like "0x0012317D6209" + # Turn that into a 12-character string, then add colons + raw_mac = line.split()[0].replace("0x", "").strip() + return ":".join(raw_mac[i : i + 2] for i in range(0, len(raw_mac), 2)) + + return None + + class UuidLanscan(Method): """ Uses Python's :func:`uuid._find_mac` function to get the MAC address @@ -1099,7 +1132,7 @@ def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 ArpingHost, SysIfaceFile, FcntlIface, - UuidLanscan, + LanscanIface, GetmacExe, IpconfigExe, WmicExe, diff --git a/pyproject.toml b/pyproject.toml index 4fdbdc0..c5472a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -217,7 +217,7 @@ addopts = "--tb=short" # --- Codespell configuration --- [tool.codespell] check-filenames = true -skip = '*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist,built_docs,.pytest_cache,.pdm-build,manpage,html_docs' +skip = '*.log,*.out,build,dist,*.pyc,*.class,*.git,*.vagrant,*.tox,*.egg-info,*.idea,.vscode,_build,.doctrees,htmlcov,.venv,.mypy_cache,deb_dist,built_docs,.pytest_cache,.pdm-build,manpage,html_docs,third_party' ignore-words-list = 'ether,Expresso,expresso,Espresso,espresso' diff --git a/tests/test_methods.py b/tests/test_methods.py index bab09d0..58b1a65 100644 --- a/tests/test_methods.py +++ b/tests/test_methods.py @@ -568,3 +568,31 @@ def test_uuid_lanscan(mocker): mocker.patch("getmac.utils.check_command", return_value=True) assert getmac.UuidLanscan().test() is True utils.check_command.assert_called_once_with("lanscan") + + +@pytest.mark.parametrize( + ("mac", "iface", "sample_file"), + [ + # hpux: lanscan -iap + ("00:17:A4:77:08:A4", "lan1", "third_party/glpi_agent/hpux/lanscan/hpux"), + ("00:17:A4:77:08:A2", "lan6", "third_party/glpi_agent/hpux/lanscan/hpux"), + ("00:17:A4:77:08:EA", "snap31", "third_party/glpi_agent/hpux/lanscan/hpux"), + ("00:00:00:00:00:00", "lan901", "third_party/glpi_agent/hpux/lanscan/hpux"), + ("00:00:00:00:00:00", "lan904", "third_party/glpi_agent/hpux/lanscan/hpux"), + # hpux1: lanscan -iap + ("00:16:35:3E:AC:5C", "lan0", "third_party/glpi_agent/hpux/lanscan/hpux1"), + # hpux2: lanscan -iap + ("00:16:35:3E:AC:44", "lan0", "third_party/glpi_agent/hpux/lanscan/hpux2"), + ("00:16:35:3E:AC:45", "lan1", "third_party/glpi_agent/hpux/lanscan/hpux2"), + ], +) +def test_lanscan_iface_samples(benchmark, mocker, get_sample, mac, iface, sample_file): + content = get_sample(sample_file) + mocker.patch("getmac.utils.popen", return_value=content) + + assert mac == benchmark(getmac.LanscanIface().get, arg=iface) + + assert not getmac.LanscanIface().get("lo0") + assert not getmac.LanscanIface().get("lan") + assert not getmac.LanscanIface().get("lan100") + assert not getmac.LanscanIface().get("lan90") From c14a212546c108f3aff3daf5a4aed6b2817a601c Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 29 Dec 2025 23:25:57 -0700 Subject: [PATCH 103/112] refactor!: remove UuidLanscan method --- CHANGELOG.md | 3 ++- docs/misc_docs/TODO.md | 7 +++++-- getmac/getmac.py | 28 ---------------------------- getmac/utils.py | 13 ------------- tests/test_methods.py | 17 +---------------- tests/test_utils.py | 5 ----- 6 files changed, 8 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2fa155..c685300 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,8 @@ * Removed support for Jython. As of Dec 2025, [Jython](https://github.com/jython/jython) still does not support Python 3. If and when it supports Python 3, I'll re-add support for it. * Removed explicit support for IronPython. [IronPython3 exists](https://github.com/IronLanguages/ironpython3), however I don't have a way to test it in CI. If someone knows of a way to test it in GitHub actions, let me know, and I'm happy to re-add explicit support. * Removed RPM packaging. This wasn't being used anywhere, and hasn't been updated since version 0.6.0. Way back when, I dreamt of putting this in OS package repositories. However, I've now learned that's up to the maintainers of those repos (e.g., Ubuntu or Fedora maintainers), not me. -* Remove `UuidArpGetnode` method. It's quite slow (performs up to 3 subprocess calls internally) and the functionality is already implemented by `ArpVariousArgs`. +* Removed `UuidArpGetnode` method. It's quite slow (performs up to 3 subprocess calls internally) and the functionality is already implemented by `ArpVariousArgs`. +* Removed `UuidLanscan` method. This has been replaced with the new `LanscanIface` method. Some UUID internal function names changed with Python 3.9, and it made more sense to implement from scratch in getmac instead of continuing to rely on CPython-specific private functions. ### Dev diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index 126b36a..82ff52f 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -46,7 +46,7 @@ ## Enhancements/fixes/misc. - [x] Python 3.13 + 3.14 -- [ ] Fix `UuidLanscan` for Python 3.9+ +- [x] Fix `UuidLanscan` for Python 3.9+ - [ ] [issue #76](https://github.com/GhostofGoes/getmac/issues/76): get_mac_address() is caching an old mac address, no longer present in local ARP - get_mac_address() is caching an old mac address for a given IP, even when it has timeout from OS ARP table. Only an explicit delete of the ARP entry on the OS make it return '00:00:00:00:00:00' again. - Fix is to check that the flag != 0x0, which should do the trick, unless there's an edge case that it misses. @@ -145,7 +145,10 @@ This is going to be a bit more complicated since the highest metric routes are g - [ ] Profiling: CPU usage, memory usage, run time/load time ## Misc. -- [ ] HP-UX. Detect HP-UX. Default to `lan0` if default interface can't be found. Add `lanscan` and `nwmgr` support. +- [ ] HP-UX. + - Detect HP-UX + - Default to `lan0` if default interface can't be found + - Add `nwmgr` support - [ ] Add ability to match user-provided arguments case-insensitively - [ ] Add ability to get the mac address of a Python socket's interface (`socket.socket`) - [ ] Test against non-ethernet interfaces (WiFi, LTE, etc.) diff --git a/getmac/getmac.py b/getmac/getmac.py index 656a5a7..3f9272b 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -516,34 +516,6 @@ def get(self, arg: str) -> Optional[str]: return None -class UuidLanscan(Method): - """ - Uses Python's :func:`uuid._find_mac` function to get the MAC address - using the ``lanscan`` command on platforms that support it (HP-UX). - """ - - platforms = {"other"} - method_type = "iface" - - def test(self) -> bool: - try: - from uuid import _find_mac # type: ignore # noqa: F401 - - return utils.check_command("lanscan") - except Exception: - return False - - def get(self, arg: str) -> Optional[str]: - from uuid import _find_mac # type: ignore - - mac = _find_mac("lanscan", "-ai", [arg.encode()], lambda i: 0) # noqa: ARG005 - - if mac: - return utils.uuid_convert(mac) - - return None - - class FcntlIface(Method): """ Uses :func:`fcntl.ioctl` to get the MAC address of a network diff --git a/getmac/utils.py b/getmac/utils.py index e8791eb..33506f3 100644 --- a/getmac/utils.py +++ b/getmac/utils.py @@ -231,19 +231,6 @@ def call_proc(executable: str, args: str) -> str: return output -def uuid_convert(mac: int) -> str: - """ - Convert value output from :mod:`uuid` internal function into a string. - - Args: - mac: integer value returned from a :mod:`uuid` function - - Returns: - String with colon-separated MAC address - """ - return ":".join(("%012X" % mac)[i : i + 2] for i in range(0, 12, 2)) - - def fetch_ip_using_dns() -> str: """ Determine the IP address of the default network interface. diff --git a/tests/test_methods.py b/tests/test_methods.py index 58b1a65..9dd68a3 100644 --- a/tests/test_methods.py +++ b/tests/test_methods.py @@ -554,22 +554,6 @@ def test_fcntl_iface(mocker): m.assert_called_once_with(socket.AF_INET, socket.SOCK_DGRAM) -@pytest.mark.skipif( - sys.version_info[0] == 3 and sys.version_info[1] >= 9, - reason="Python 3.9+ doesn't have uuid._find_mac", -) -def test_uuid_lanscan(mocker): - mocker.patch("uuid._find_mac", return_value=2482700837424) - assert getmac.UuidLanscan().get("en1") == "02:42:0C:80:62:30" - mocker.patch("uuid._find_mac", return_value=None) - assert getmac.UuidLanscan().get("10.0.0.1") is None - assert getmac.UuidLanscan().get("en0") is None - - mocker.patch("getmac.utils.check_command", return_value=True) - assert getmac.UuidLanscan().test() is True - utils.check_command.assert_called_once_with("lanscan") - - @pytest.mark.parametrize( ("mac", "iface", "sample_file"), [ @@ -592,6 +576,7 @@ def test_lanscan_iface_samples(benchmark, mocker, get_sample, mac, iface, sample assert mac == benchmark(getmac.LanscanIface().get, arg=iface) + assert not getmac.LanscanIface().get("") assert not getmac.LanscanIface().get("lo0") assert not getmac.LanscanIface().get("lan") assert not getmac.LanscanIface().get("lan100") diff --git a/tests/test_utils.py b/tests/test_utils.py index 681dc74..56271fd 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -55,11 +55,6 @@ def test_call_proc(mocker): m.assert_called_once_with(["CMD", "arg1", "arg2"], stderr="DEVNULL", env="ENV") -def test_uuid_convert(): - assert utils.uuid_convert(2482700837424) == "02:42:0C:80:62:30" - assert utils.uuid_convert(278094213753144) == "FC:EC:DA:D3:29:38" - - def test_fetch_ip_using_dns(mocker): m = mocker.patch("socket.socket.__enter__") m.return_value.getsockname.return_value = ("1.2.3.4", 51327) From 9eec242759e03a11a243b872d2f9f3ce33421a94 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 30 Dec 2025 00:10:34 -0700 Subject: [PATCH 104/112] feat: HP-UX detection and default interface --- CHANGELOG.md | 4 ++- README.md | 10 ++++--- docs/misc_docs/TODO.md | 65 ++++++++++++++++++++++-------------------- getmac/getmac.py | 7 +++-- getmac/variables.py | 2 ++ tests/test_getmac.py | 10 +++++-- tests/test_methods.py | 2 -- 7 files changed, 58 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c685300..1d44b88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,9 @@ * Handle `bytes` values for `interface`, `ip`, `ip6`, and `hostname` arguments to `get_mac_address()`. This will just call `decode("utf-8")` on the bytes. This is to ease usage with certain libraries and functions that return strings as bytes. - If you pass it some weird bytes that aren't decodable as utf-8, it's obviously going to break. Don't be dumb :) * Added manpage for the command line interface (`getmac.1`) -* New method, `LanscanIface`, a proper implementation of `lanscan -ia` for HP-UX systems. This replaces the functionality of the removed `UuidLanscan` method, and is loosely based on CPython's `uuid._lanscan_getnode()` implementation. +* Improved HP-UX support + * New method, `LanscanIface`, a proper implementation of `lanscan -ia` for HP-UX systems. This replaces the functionality of the removed `UuidLanscan` method, and is loosely based on CPython's `uuid._lanscan_getnode()` implementation. + * Added platform detection for HP-UX and a default to `lan0` interface ### Changed * BREAKING CHANGE: refactored how settings are handled. Instead of module-level globals, they're implemented in a Settings singleton in `getmac.settings`. For example, `getmac.getmac.PORT` should now be `getmac.settings.PORT`. diff --git a/README.md b/README.md index b2bc6c3..cd67651 100644 --- a/README.md +++ b/README.md @@ -167,14 +167,16 @@ getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost ## Commands and techniques by platform - Windows - Commands: `getmac.exe`, `ipconfig.exe`, `arp.exe`, `wmic.exe` - - Libraries: `uuid`, `ctypes`, `socket` + - Libraries: `ctypes`, `socket` - Linux/Unix - - Commands: `arp`, `ip`, `ifconfig`, `netstat`, `ip link`, `lanscan`, `arping` (both iputils and Habet's variants) - - Libraries: `uuid`, `fcntl`, `socket` + - Commands: `arp`, `ip`, `ifconfig`, `netstat`, `ip link`, `arping` (both iputils and Habet's variants) + - Libraries: `fcntl`, `socket` - Files: `/sys/class/net/{iface}/address`, `/proc/net/arp` - Default interfaces: `/proc/net/route`, `route`, `ip route list` +- HP-UX: + - Commands: `lanscan` - Mac OSX (Darwin) - - `networksetup`, `arping` + - Commands: `networksetup`, `arping` - Many of the same commands as Linux - WSL - Windows commands are used for remote hosts diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index 82ff52f..e11a857 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -20,6 +20,7 @@ - directly test methods via a `Method.parse()` function - add `Method.parse()` that handles the parsing of command - [ ] Improve CLI tests to ensure output is what's expected (e.g. ensure `--override-port` logs a warning and the value actually gets overridden) +- [ ] Add tests for more samples (see TODOs at top of test_methods.py, plus new third-party samples) ## Features - [x] Support `ipaddress` objects, `IPv4Address` and `IPv6Address` @@ -50,13 +51,6 @@ - [ ] [issue #76](https://github.com/GhostofGoes/getmac/issues/76): get_mac_address() is caching an old mac address, no longer present in local ARP - get_mac_address() is caching an old mac address for a given IP, even when it has timeout from OS ARP table. Only an explicit delete of the ARP entry on the OS make it return '00:00:00:00:00:00' again. - Fix is to check that the flag != 0x0, which should do the trick, unless there's an edge case that it misses. -- [ ] Cleanup `ifconfig` methods - - [ ] Split `IfconfigOther` into IfconfigWithArg/IfconfigNoArg - - [ ] Combine `IfconfigEther` into other Ifconfig methods - - [ ] Improve unit test coverage and platform markers -- [ ] `IpLinkIface`: improve regex to not need extra portion for no arg -- [ ] Add new regexes to `IpLinkIface` and improve it's parsing so it's more robust, especially on Android -- [ ] finer-grained platform support identification for methods by versions/releases, e.g. Windows 7 vs 10, Ubuntu 12 vs 20 ## Before releasing - [ ] Add a deprecation warning to `get-mac` package, don't publish it for 1.0.0 @@ -141,14 +135,34 @@ This is going to be a bit more complicated since the highest metric routes are g - [ ] Determine best remote host detection methods, split off not-applicable commands. - [ ] Darwin hostnames? Does it have arp file? (Do less work) - [ ] Mac: `ndp -a` to get IPv6 network neighbors (NDP table) -## Performance + +## Misc platforms +- [ ] Properly support WSL2 +- [ ] `nwmgr` for HP-UX +- [ ] Properly implement and test HP-UX for `netstat` and `ifconfig`, add `hp-ux` to `platforms` for corresponding Methods +- [ ] FreeBSD default interface: `route get default` +- [ ] Support NetBSD + - platform: `netbsd` + - default interface: `wm0` + - ip: "route -nq show", "netstat -r", arp -a + - default interface via `route get default`? +- [ ] Support Solaris + - platform: `sunos` + - default interface; `e1000g0` (NOTE: likely because this is in Vagrant VM) + - `ifconfig` with no arguments DOES NOT work, need `ifconfig -a` + - `netstat` doesn't work with `-e`, but does work with no arguments, `-a` and `-i`. `-n` prevents hostnames from resolving, which is faster. `-i` gives the shortest output (and is fastest), but doesn't give us a MAC address. Providing the interface as an argument also doesn't work to get a MAC (`netstat -a -I e1000g0`). + - default interface via `route get default`? + - no `ip` command + + +# Performance - [ ] Profiling: CPU usage, memory usage, run time/load time +- [ ] Parameterize regexes? (is this any faster?) +- [ ] Cache method checks (maybe move this to 1.1.0 release?) Save a string with the names of methods. Save to: file (location configurable via environment variable or option). Read from: file, environment variable, file pointed to by environment variable. Add a flag to control this behavior and location of the cache. Document the behavior. +- [ ] Refactor to build a local state of the interfaces on the system, and use that as fallback for default lookup of interface with no name. Could also include MACs for faster lookup of future interface queries. Similar to how `netifaces` works, with a dict with interface infos. Properly address https://github.com/GhostofGoes/getmac/issues/78 + -## Misc. -- [ ] HP-UX. - - Detect HP-UX - - Default to `lan0` if default interface can't be found - - Add `nwmgr` support +# Misc. - [ ] Add ability to match user-provided arguments case-insensitively - [ ] Add ability to get the mac address of a Python socket's interface (`socket.socket`) - [ ] Test against non-ethernet interfaces (WiFi, LTE, etc.) @@ -161,7 +175,6 @@ This is going to be a bit more complicated since the highest metric routes are g # Dev -- [ ] Automatically publish to PyPI when publishing a release on GitHub - [ ] Add typing stubs to [typeshed](https://github.com/python/typeshed) once getmac 1.0.0 is released ([guide](https://github.com/python/typeshed/blob/master/CONTRIBUTING.md)) - [ ] Add to Conda Forge ([example here](https://github.com/conda-forge/staged-recipes/pull/26828/files)) - [x] Add [isort](https://pycqa.github.io/isort/) (requires python 3.8+) @@ -169,33 +182,23 @@ This is going to be a bit more complicated since the highest metric routes are g # Post-1.0.0 -- [ ] Properly support WSL2 +- [ ] Cleanup `ifconfig` methods + - [ ] Split `IfconfigOther` into IfconfigWithArg/IfconfigNoArg + - [ ] Combine `IfconfigEther` into other Ifconfig methods + - [ ] Improve unit test coverage and platform markers +- [ ] `IpLinkIface`: improve regex to not need extra portion for no arg +- [ ] Add new regexes to `IpLinkIface` and improve it's parsing so it's more robust, especially on Android +- [ ] finer-grained platform support identification for methods by versions/releases, e.g. Windows 7 vs 10, Ubuntu 12 vs 20 - [ ] address all TODOs in the code - [ ] implement proper default interface detection on Windows -- [ ] FreeBSD default interface: `route get default` -- [ ] Support NetBSD - - platform: `netbsd` - - default interface: `wm0` - - ip: "route -nq show", "netstat -r", arp -a - - default interface via `route get default`? -- [ ] Support Solaris - - platform: `sunos` - - default interface; `e1000g0` (NOTE: likely because this is in Vagrant VM) - - `ifconfig` with no arguments DOES NOT work, need `ifconfig -a` - - `netstat` doesn't work with `-e`, but does work with no arguments, `-a` and `-i`. `-n` prevents hostnames from resolving, which is faster. `-i` gives the shortest output (and is fastest), but doesn't give us a MAC address. Providing the interface as an argument also doesn't work to get a MAC (`netstat -a -I e1000g0`). - - default interface via `route get default`? - - no `ip` command - [ ] Support IPv6 hosts: https://www.practicalcodeuse.com/how-to-arp-a-in-ipv6 - [ ] Support IPv6 remote hosts on windows, and IPv4+IPv6 remote hosts on WSL (see "Platform support" section in this document) -- [ ] Refactor to build a local state of the interfaces on the system, and use that as fallback for default lookup of interface with no name. Could also include MACs for faster lookup of future interface queries. Similar to how `netifaces` works, with a dict with interface infos. Properly address https://github.com/GhostofGoes/getmac/issues/78 - [ ] New method for "ip addr"? (this would be useful for CentOS and others as a fallback) - [ ] Method-specific loggers? dynamically set logger name based on subclass name, so we don't have to manually set it in the string - [ ] Use `__import__()` or `importlib`? -- [ ] Parameterize regexes? (is this any faster?) - [ ] Add support for Unix and Windows interface indices as a separate argument to `get_mac_address`. On Windows, we could use `wmic`, while on Unix and Python 3 we can use `socket.if_indextoname()`. - [ ] API to add/remove methods at runtime (including new, custom methods) - [ ] Reduce duplication, for example "if not arg: return None" -- [ ] Cache method checks (maybe move this to 1.1.0 release?) Save a string with the names of methods. Save to: file (location configurable via environment variable or option). Read from: file, environment variable, file pointed to by environment variable. Add a flag to control this behavior and location of the cache. Document the behavior. - [ ] Add a "net_ok" argument, check network_request attribute on method in CACHE, if not then keep checking for method in FALLBACK_CACHE that has network_request. - [ ] Add ability to specify what methods to use via function argument and CLI argument - [ ] Add ability to force platform name (e.g. `linux`) via function argument and CLI argument diff --git a/getmac/getmac.py b/getmac/getmac.py index 3f9272b..52cefce 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -61,6 +61,7 @@ class Method: "openbsd", "freebsd", "sunos", + "hp-ux", "other", } """ @@ -485,12 +486,12 @@ def get(self, arg: str) -> Optional[str]: class LanscanIface(Method): """ - Uses the ``lanscan`` command to get the MAC address of a network interface. + Uses the ``lanscan`` command to get the MAC address of a network interface on HP-UX. This is adopted from Python's :mod:`uuid` module's ``_lanscan_getnode`` function. """ - platforms = {"other"} + platforms = {"hp-ux"} method_type = "iface" def test(self) -> bool: @@ -1749,6 +1750,8 @@ def get_mac_address( gvars.DEFAULT_IFACE = "em0" elif not gvars.DEFAULT_IFACE and consts.DARWIN: # OSX, maybe? gvars.DEFAULT_IFACE = "en0" + elif not gvars.DEFAULT_IFACE and consts.HPUX: + gvars.DEFAULT_IFACE = "lan0" elif not gvars.DEFAULT_IFACE: gvars.DEFAULT_IFACE = "eth0" diff --git a/getmac/variables.py b/getmac/variables.py index b52f84d..4a3ce95 100644 --- a/getmac/variables.py +++ b/getmac/variables.py @@ -56,6 +56,7 @@ class Constants(VarsClass): FREEBSD: Final[bool] = _SYST == "FreeBSD" NETBSD: Final[bool] = _SYST == "NetBSD" SOLARIS: Final[bool] = _SYST == "SunOS" + HPUX: Final[bool] = _SYST == "HP-UX" BSD: Final[bool] = OPENBSD or FREEBSD or NETBSD """ @@ -125,6 +126,7 @@ class Constants(VarsClass): - freebsd - netbsd - sunos + - hp-ux - Any other values that can be returned by :func:`platform.uname`, converted to lowercase. diff --git a/tests/test_getmac.py b/tests/test_getmac.py index 94abc42..3f45942 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -65,6 +65,7 @@ def test_get_instance_from_cache(mocker): def test_swap_method_fallback(mocker): mocker.patch("getmac.getmac.METHOD_CACHE", {"ip4": getmac.ArpExe()}) mocker.patch("getmac.getmac.FALLBACK_CACHE", {"ip4": [getmac.CtypesHost()]}) + assert getmac._swap_method_fallback("ip4", "ArpExe") assert not getmac._swap_method_fallback("ip4", "InvalidMethod") assert getmac._swap_method_fallback("ip4", "CtypesHost") @@ -81,10 +82,11 @@ def test_initialize_method_cache_valid_types(mocker, method_type): {"ip4": None, "ip6": None, "iface": None, "default_iface": None}, ) mocker.patch("getmac.getmac.FALLBACK_CACHE", {}) - mocker.patch.object(consts, "PLATFORM", "linux") + assert getmac.initialize_method_cache(method_type) assert getmac.METHOD_CACHE[method_type] is not None - if method_type in ["ip4", "ip6"]: + + if method_type in ["ip4", "ip6"] and consts.PLATFORM == "linux": assert getmac.FALLBACK_CACHE[method_type] @@ -95,6 +97,7 @@ def test_initialize_method_cache_initialized(mocker): ) mocker.patch("getmac.getmac.FALLBACK_CACHE", {}) mocker.patch.object(consts, "PLATFORM", "linux") + assert getmac.initialize_method_cache("ip4") assert str(getmac.METHOD_CACHE["ip4"]) == "ArpFile" assert isinstance(getmac.METHOD_CACHE["ip4"], getmac.Method) @@ -107,6 +110,7 @@ def test_initialize_method_cache_bad_type(mocker): ) mocker.patch("getmac.getmac.FALLBACK_CACHE", {}) mocker.patch.object(consts, "PLATFORM", "linux") + with pytest.warns(RuntimeWarning): assert not getmac.initialize_method_cache("invalid_method_type") with pytest.warns(RuntimeWarning): @@ -123,6 +127,7 @@ def test_initialize_method_cache_platform_override(mocker): mocker.patch.object(consts, "PLATFORM", "windows") mocker.patch.object(settings, "OVERRIDE_PLATFORM", "darwin") mocker.patch("getmac.utils.check_command", return_value=True) + assert getmac.initialize_method_cache("iface") assert settings.OVERRIDE_PLATFORM == "darwin" assert consts.PLATFORM == "windows" @@ -138,6 +143,7 @@ def test_initialize_method_cache_no_network_request(mocker): mocker.patch.object(consts, "PLATFORM", "linux") mocker.patch("getmac.utils.check_command", return_value=True) mocker.patch("getmac.utils.check_path", return_value=True) + assert getmac.initialize_method_cache("ip4", network_request=False) assert consts.PLATFORM == "linux" assert isinstance(getmac.METHOD_CACHE["ip4"], getmac.ArpFile) diff --git a/tests/test_methods.py b/tests/test_methods.py index 9dd68a3..58259a0 100644 --- a/tests/test_methods.py +++ b/tests/test_methods.py @@ -1,6 +1,5 @@ import platform import socket -import sys from subprocess import CalledProcessError import pytest @@ -576,7 +575,6 @@ def test_lanscan_iface_samples(benchmark, mocker, get_sample, mac, iface, sample assert mac == benchmark(getmac.LanscanIface().get, arg=iface) - assert not getmac.LanscanIface().get("") assert not getmac.LanscanIface().get("lo0") assert not getmac.LanscanIface().get("lan") assert not getmac.LanscanIface().get("lan100") From e15fa758ab6d3db7317446ed0cef979b0666b740 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 30 Dec 2025 13:19:40 -0700 Subject: [PATCH 105/112] fix: improve ArpVariousArgs and testing --- docs/misc_docs/TODO.md | 52 +++++++++++++++++++++++------------------- getmac/getmac.py | 23 ++++++++++++++----- tests/test_methods.py | 22 ++++++++++++++++-- 3 files changed, 65 insertions(+), 32 deletions(-) diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index e11a857..f779aae 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -20,13 +20,11 @@ - directly test methods via a `Method.parse()` function - add `Method.parse()` that handles the parsing of command - [ ] Improve CLI tests to ensure output is what's expected (e.g. ensure `--override-port` logs a warning and the value actually gets overridden) -- [ ] Add tests for more samples (see TODOs at top of test_methods.py, plus new third-party samples) +- [ ] Add tests for more samples (new third-party samples) ## Features - [x] Support `ipaddress` objects, `IPv4Address` and `IPv6Address` - [x] Add new method: `get_default_interface()`. This leverages the default interface detection methods to expose a helpful public API. -- [ ] [issue 77](https://github.com/GhostofGoes/getmac/issues/77): Feature: get all mac addresses - - "As I was thinking of adding support to jaraco.net for supporting macOS devices (IP addresses and mac addresses), I thought getmac might be a helpful solution, but as I delved into it, I could see that getmac only returns a single mac, even though there may be multiple on a host. It would be nice if getmac could abstract some of its behaviors, mainly to allow a user to query for all mac addresses represented by the host." ## Breaking changes (or potentially breaking) - [x] Replace the `UuidArpGetNode` method. It calls 3 commands and is quite inefficient. It's functionality is already implemented by `ArpVariousArgs`. @@ -37,14 +35,28 @@ - [ ] **API changes** (technically speaking) - Add argument to `get_mac_address()` to force the platform used (e.g. `platform_override="linux"`) - Also add CLI argument to configure this - - Add argument to `get_mac_address()` to force a specific method to be used + - Add argument to `get_mac_address()` to force a specific method(s) to be used - Passing a string with the name of a method class (e.g. `"ArpFile"`), this will be dynamically looked up from the list of available methods. This will NOT check if the method works by default! - Passing a subclass of `getmac.Method` - Passing an instance of a subclass of `getmac.Method` - - Add a CLI argument to reference class by name + - List/Iterable of methods (as above, string/subclass/instance) + - Add a CLI argument to reference class by name/names - Add ability to exclude methods. Just remove them from METHODS list so they never get used. Useful for testing specific methods or working around buggy methods. + - Add a `net_ok` argument, check `network_request` attribute on method in CACHE, if not then keep checking for method in FALLBACK_CACHE that has `network_request`. - Document these features in the README/docs, including the CLI arguments +``` +methods=None +type: Optional[List[Union[str, Method, Type[Method]]]] +methods (list): Optional list of methods to use for MAC address lookup. + This will override the default methods that are auto-determined based on + platform inspection and testing, and will be used regardless of whether + they work or not. These can be names of method classes as strings + (``"ArpFile"``), ``Method`` subclasses (``ArpFile``), + or instances of ``Method`` subclasses (``ArpFile()``). +``` + + ## Enhancements/fixes/misc. - [x] Python 3.13 + 3.14 - [x] Fix `UuidLanscan` for Python 3.9+ @@ -100,6 +112,15 @@ with a slow run since it tries every method before failing. - [ ] **Security**. Spend some quality time ensuring our sources of input (the arguments to `get_mac_address()`) don't result in unexpected code execution. A lot of stuff is running system commands, so we should focus the most effort on the `subprocess.Popen()` calls. +# API Features +- [ ] [issue 77](https://github.com/GhostofGoes/getmac/issues/77): Feature: get all mac addresses + - "As I was thinking of adding support to jaraco.net for supporting macOS devices (IP addresses and mac addresses), I thought getmac might be a helpful solution, but as I delved into it, I could see that getmac only returns a single mac, even though there may be multiple on a host. It would be nice if getmac could abstract some of its behaviors, mainly to allow a user to query for all mac addresses represented by the host." +- [ ] Add support for Unix and Windows interface indices as a separate argument to `get_mac_address`. On Windows, we could use `wmic`, while on Unix and Python 3 we can use `socket.if_indextoname()`. +- [ ] Add ability to match user-provided arguments case-insensitively +- [ ] Add ability to get the mac address of a Python socket's interface (`socket.socket`) +- [ ] API to add/remove methods at runtime (including new, custom methods) + - [ ] Document this API and how the method API functions work more generally + # Platform support ## Windows @@ -162,11 +183,8 @@ This is going to be a bit more complicated since the highest metric routes are g - [ ] Refactor to build a local state of the interfaces on the system, and use that as fallback for default lookup of interface with no name. Could also include MACs for faster lookup of future interface queries. Similar to how `netifaces` works, with a dict with interface infos. Properly address https://github.com/GhostofGoes/getmac/issues/78 -# Misc. -- [ ] Add ability to match user-provided arguments case-insensitively -- [ ] Add ability to get the mac address of a Python socket's interface (`socket.socket`) +# Testing - [ ] Test against non-ethernet interfaces (WiFi, LTE, etc.) -- [ ] Create a script to collect samples for all relevant commands on a platform and save output into the appropriately named sub-directory in `samples/`. # Documentation @@ -179,6 +197,7 @@ This is going to be a bit more complicated since the highest metric routes are g - [ ] Add to Conda Forge ([example here](https://github.com/conda-forge/staged-recipes/pull/26828/files)) - [x] Add [isort](https://pycqa.github.io/isort/) (requires python 3.8+) - [ ] Move method classes into a separate file +- [ ] Create a script to collect samples for all relevant commands on a platform and save output into the appropriately named sub-directory in `samples/`. # Post-1.0.0 @@ -196,19 +215,4 @@ This is going to be a bit more complicated since the highest metric routes are g - [ ] New method for "ip addr"? (this would be useful for CentOS and others as a fallback) - [ ] Method-specific loggers? dynamically set logger name based on subclass name, so we don't have to manually set it in the string - [ ] Use `__import__()` or `importlib`? -- [ ] Add support for Unix and Windows interface indices as a separate argument to `get_mac_address`. On Windows, we could use `wmic`, while on Unix and Python 3 we can use `socket.if_indextoname()`. -- [ ] API to add/remove methods at runtime (including new, custom methods) - [ ] Reduce duplication, for example "if not arg: return None" -- [ ] Add a "net_ok" argument, check network_request attribute on method in CACHE, if not then keep checking for method in FALLBACK_CACHE that has network_request. -- [ ] Add ability to specify what methods to use via function argument and CLI argument -- [ ] Add ability to force platform name (e.g. `linux`) via function argument and CLI argument -``` -methods=None -type: Optional[List[Union[str, Method, Type[Method]]]] -methods (list): Optional list of methods to use for MAC address lookup. - This will override the default methods that are auto-determined based on - platform inspection and testing, and will be used regardless of whether - they work or not. These can be names of method classes as strings - (``"ArpFile"``), ``Method`` subclasses (``ArpFile``), - or instances of ``Method`` subclasses (``ArpFile()``). -``` diff --git a/getmac/getmac.py b/getmac/getmac.py index 52cefce..a4105c4 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -210,6 +210,9 @@ class ArpVariousArgs(Method): _regex_std: Final[str] = r"\)\s+at\s+" + consts.MAC_RE_COLON _regex_darwin: Final[str] = r"\)\s+at\s+" + consts.MAC_RE_SHORT + + # Possible arp arguments to try + # Second element indicates whether to include IP as argument _args = ( ("", True), # "arp 192.168.1.1" # Linux @@ -220,9 +223,11 @@ class ArpVariousArgs(Method): ("-a", False), # "arp -a" ("-a", True), # "arp -a 192.168.1.1" ) + + # If arguments have been tested _args_tested: bool = False + # arguments that worked _good_pair: Union[Tuple, Tuple[str, bool]] = () - _good_regex: str = _regex_darwin if consts.DARWIN else _regex_std def test(self) -> bool: return utils.check_command("arp") @@ -255,11 +260,15 @@ def get(self, arg: str) -> Optional[str]: f"({pair_to_test[0]}, {pair_to_test[1]}): {ex}" ) + # if no valid argument pair was found, mark unusable if not self._good_pair: self.unusable = True return None + + # Mark args as tested to prevent re-testing on subsequent calls self._args_tested = True + # If tests aren't run (e.g. they ran previously), then run the good pair now if not command_output: # if True, then include IP as a command argument cmd_args = [self._good_pair[0]] @@ -269,11 +278,13 @@ def get(self, arg: str) -> Optional[str]: command_output = utils.popen("arp", " ".join(cmd_args)) - escaped = re.escape(arg) - _good_regex = ( - self._regex_darwin if consts.DARWIN or consts.SOLARIS else self._regex_std - ) # type: str - return utils.search(r"\(" + escaped + _good_regex, command_output) + # Do this here for testing reasons + if consts.DARWIN or consts.SOLARIS: + regex = r"\(" + re.escape(arg) + self._regex_darwin + else: + regex = r"\(" + re.escape(arg) + self._regex_std + + return utils.search(regex, command_output) class ArpExe(Method): diff --git a/tests/test_methods.py b/tests/test_methods.py index 58259a0..bb0a5e4 100644 --- a/tests/test_methods.py +++ b/tests/test_methods.py @@ -5,7 +5,7 @@ import pytest from getmac import getmac, utils -from getmac.variables import consts +from getmac.variables import consts, settings # TODO: freebsd11/netstat_-ia.out # TODO: netstat_-ian_aix.out @@ -504,7 +504,7 @@ def test_defaultifaceroutegetcommand_samples( ("52:54:00:12:35:02", "10.0.2.2", "solaris10/arp_10-0-2-2.out"), ], ) -def test_arp_various_args(benchmark, mocker, get_sample, mac, ip, sample_file): +def test_arp_various_args_samples(benchmark, mocker, get_sample, mac, ip, sample_file): content = get_sample(sample_file) mocker.patch("getmac.utils.popen", return_value=content) if "OSX" in sample_file: @@ -530,6 +530,24 @@ def test_arp_various_args(benchmark, mocker, get_sample, mac, ip, sample_file): assert mac == result +def test_arp_various_args_edge_cases(mocker, get_sample): + assert not getmac.ArpVariousArgs().get("") + + mocker.patch.object(settings, "DEBUG", 1) + cpe = CalledProcessError(cmd="arp", returncode=1) + mocker.patch("getmac.utils.popen", side_effect=cpe) + assert getmac.ArpVariousArgs().get("192.0.2.1") is None + + # Not sure if IP is required on Ubuntu 18, this is just for purposes of testing + mocker.patch( + "getmac.utils.popen", return_value=get_sample("ubuntu_18.04/arp_-an.out") + ) + inst = getmac.ArpVariousArgs() + inst._args_tested = True + inst._good_pair = ("-an", True) + assert inst.get("192.168.16.2") == "00:50:56:f1:4c:50" + + def test_sys_iface_file(mocker): mocker.patch("getmac.utils.read_file", return_value="00:0c:29:b5:72:37\n") assert getmac.SysIfaceFile().get("ens33") == "00:0c:29:b5:72:37\n" From abf50b26908694d88c3056d6eddc83a1b653d0cb Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 30 Dec 2025 14:02:30 -0700 Subject: [PATCH 106/112] refactor: route path as class attribute --- getmac/getmac.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/getmac/getmac.py b/getmac/getmac.py index a4105c4..c9d9980 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -963,11 +963,13 @@ class DefaultIfaceLinuxRouteFile(Method): platforms = {"linux", "wsl"} method_type = "default_iface" + _path: Final[str] = "/proc/net/route" + def test(self) -> bool: - return utils.check_path("/proc/net/route") + return utils.check_path(self._path) def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 - data = utils.read_file("/proc/net/route") + data = utils.read_file(self._path) if data is not None and len(data) > 1: for line in data.split("\n")[1:-1]: @@ -989,10 +991,10 @@ def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 if settings.DEBUG: gvars.log.debug( "Failed to find default interface in data from " - "'/proc/net/route', no destination of '00000000' was found" + f"'{self._path}', no destination of '00000000' was found" ) elif settings.DEBUG: - gvars.log.warning("No data from /proc/net/route") + gvars.log.warning(f"No data from {self._path}") return None From 84477b4c0f7e473b6e09ffbe2daa54f83491e81a Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 30 Dec 2025 14:02:41 -0700 Subject: [PATCH 107/112] test: improve test coverage --- tests/test_methods.py | 135 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 26 deletions(-) diff --git a/tests/test_methods.py b/tests/test_methods.py index bb0a5e4..ba59f49 100644 --- a/tests/test_methods.py +++ b/tests/test_methods.py @@ -26,8 +26,8 @@ def test_darwinnetworksetupiface(benchmark, mocker, get_sample): mocker.patch("getmac.utils.popen", return_value="") assert not getmac.DarwinNetworksetupIface().get("en0") - mocker.patch("getmac.utils.check_command", return_value=True) - assert getmac.DarwinNetworksetupIface().test() is True + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.DarwinNetworksetupIface().test() is False utils.check_command.assert_called_once_with("networksetup") @@ -57,6 +57,15 @@ def test_ifconfigether_darwin(benchmark, mocker, get_sample, mac, sample_file): assert not getmac.IfconfigEther().get("utun0") +def test_ifconfigother_edge_cases(mocker): + # Test the test function + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.IfconfigOther().test() is False + utils.check_command.assert_called_once_with("ifconfig") + + assert getmac.IfconfigOther().get("") is None + + # TODO: several of these should be a different method without a interface arg ifconfig_samples = [ ("74:d4:35:e9:45:71", "eth0", "ifconfig.out"), @@ -116,7 +125,12 @@ def test_ifconfigwithifacearg_samples(mocker, get_sample, mac, iface, sample_fil assert mac == getmac.IfconfigWithIfaceArg().get(iface) -def test_ifconfigwithifacearg_bad_exits(mocker): +def test_ifconfigwithifacearg_edge_cases(mocker): + # Test the test function + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.IfconfigWithIfaceArg().test() is False + utils.check_command.assert_called_once_with("ifconfig") + cpe = CalledProcessError(cmd="ifconfig", returncode=1) mocker.patch("getmac.utils.popen", side_effect=cpe) assert getmac.IfconfigWithIfaceArg().get("eth0") is None @@ -158,6 +172,34 @@ def test_arping_host_busybox(benchmark, mocker, get_sample): assert "00:15:5d:20:f2:73" == benchmark(ap.get, arg="172.29.16.1") +def test_arping_host_edge_cases(mocker): + # Test the test function + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.ArpingHost().test() is False + utils.check_command.assert_called_once_with("arping") + + # No output case in _call_habets() + mocker.patch("getmac.utils.popen", return_value="") + assert not getmac.ArpingHost()._call_habets("192.168.16.254") + + # Test somewhat complex fallback logic for Habets arping + cpe = CalledProcessError( + cmd="arping -f -c 1 192.0.2.1", output=b"invalid option", returncode=1 + ) + mocker.patch("getmac.utils.popen", side_effect=cpe) + mocker.patch.object(settings, "DEBUG", 1) + + # Standard case + mocker.patch( + "getmac.getmac.ArpingHost._call_habets", return_value="00:50:56:e8:32:3c" + ) + assert getmac.ArpingHost().get("192.168.16.254") == "00:50:56:e8:32:3c" + + # Exception handling case + mocker.patch("getmac.getmac.ArpingHost._call_habets", side_effect=cpe) + assert not getmac.ArpingHost().get("192.168.16.254") + + def test_windows_10_iface_getmac_exe(benchmark, mocker, get_sample): content = get_sample("windows_10/getmac.out") mocker.patch("getmac.utils.popen", return_value=content) @@ -185,8 +227,8 @@ def test_arpexe_samples(benchmark, mocker, get_sample, mac, ip, sample_file): mocker.patch("getmac.utils.popen", return_value=content) assert mac == benchmark(getmac.ArpExe().get, arg=ip) - mocker.patch("getmac.utils.check_command", return_value=True) - assert getmac.ArpExe().test() is True + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.ArpExe().test() is False utils.check_command.assert_called_once_with("arp.exe") @@ -198,8 +240,8 @@ def test_openbsd_get_default_iface(benchmark, mocker, get_sample): mocker.patch("getmac.utils.popen", return_value="") assert not getmac.DefaultIfaceOpenBsd().get() - mocker.patch("getmac.utils.check_command", return_value=True) - assert getmac.DefaultIfaceOpenBsd().test() is True + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.DefaultIfaceOpenBsd().test() is False utils.check_command.assert_called_once_with("route") @@ -210,8 +252,8 @@ def test_openbsd_remote(benchmark, mocker, get_sample): assert "52:54:00:12:35:03" == getmac.ArpOpenbsd().get("10.0.2.3") assert "08:00:27:18:64:56" == getmac.ArpOpenbsd().get("10.0.2.15") - mocker.patch("getmac.utils.check_command", return_value=True) - assert getmac.ArpOpenbsd().test() is True + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.ArpOpenbsd().test() is False utils.check_command.assert_called_once_with("arp") @@ -220,8 +262,8 @@ def test_freebsd_get_default_iface(benchmark, mocker, get_sample): mocker.patch("getmac.utils.popen", return_value=content) assert "em0" == benchmark(getmac.DefaultIfaceFreeBsd().get) - mocker.patch("getmac.utils.check_command", return_value=True) - assert getmac.DefaultIfaceFreeBsd().test() is True + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.DefaultIfaceFreeBsd().test() is False utils.check_command.assert_called_once_with("netstat") @@ -244,8 +286,8 @@ def test_arpfreebsd_samples(benchmark, mocker, get_sample, mac, ip, sample_file) assert not getmac.ArpFreebsd().get(mac) assert not getmac.ArpFreebsd().get("em0") - mocker.patch("getmac.utils.check_command", return_value=True) - assert getmac.ArpFreebsd().test() is True + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.ArpFreebsd().test() is False utils.check_command.assert_called_once_with("arp") @@ -295,14 +337,24 @@ def test_arpfile_samples(benchmark, mocker, get_sample, mac, ip, sample_file): ("8e:8f:aa:c9:d2:8b", "fe80::8c8f:aaff:fec9:d28b", "android_9/ip_neighbor.out"), ], ) -def test_ipneighshow_samples(benchmark, mocker, get_sample, mac, ip, sample_file): +def test_ipneighborshow_samples(benchmark, mocker, get_sample, mac, ip, sample_file): content = get_sample(sample_file) mocker.patch("getmac.utils.popen", return_value=content) - assert mac == benchmark(getmac.IpNeighborShow().get, arg=ip) + assert mac == benchmark(getmac.IpNeighborShow().get, arg=ip) assert getmac.IpNeighborShow().get("bad") is None +def test_ipneighborshow_edge_cases(mocker): + # Test the test function + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.IpNeighborShow().test() is False + utils.check_command.assert_called_once_with("ip") + + mocker.patch("getmac.utils.popen", return_value="") + assert not getmac.IpNeighborShow().get("192.168.16.2") + + @pytest.mark.parametrize( ("mac", "iface", "sample_file"), [ @@ -331,21 +383,19 @@ def test_netstatiface_samples(benchmark, mocker, get_sample, mac, iface, sample_ # assert getmac.NetstatIface().get("Kernel") is None # assert getmac.NetstatIface().get("e") is None + +def test_netstatiface_edge_cases(mocker): + # Test the test function + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.NetstatIface().test() is False + utils.check_command.assert_called_once_with("netstat") + mocker.patch("getmac.utils.popen", return_value=None) assert getmac.NetstatIface().get("eth0") is None mocker.patch("getmac.utils.popen", return_value=" ") assert getmac.NetstatIface().get("eth0") is None -def test_ip_link_iface_bad_returncode(mocker, get_sample): - """Test the exception handling works for old-style ip link.""" - content = get_sample("ip_link_list.out") - cpe = CalledProcessError(cmd="", returncode=255) - mocker.patch("getmac.utils.popen", side_effect=[cpe, content]) - except_method = getmac.IpLinkIface() - assert "74:d4:35:e9:45:71" == except_method.get("eth0") - - @pytest.mark.parametrize( ("expected_mac", "iface_arg"), [ @@ -393,6 +443,20 @@ def test_iplinkiface_samples(benchmark, mocker, get_sample, mac, iface, sample_f # assert getmac.IpLinkIface().get("") is None +def test_ip_link_iface_edge_cases(mocker, get_sample): + # Test the test function + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.IpLinkIface().test() is False + utils.check_command.assert_called_once_with("ip") + + # Test the exception handling works for old-style ip link + content = get_sample("ip_link_list.out") + cpe = CalledProcessError(cmd="", returncode=255) + mocker.patch("getmac.utils.popen", side_effect=[cpe, content]) + except_method = getmac.IpLinkIface() + assert "74:d4:35:e9:45:71" == except_method.get("eth0") + + @pytest.mark.parametrize( ("expected_iface", "sample_file"), [ @@ -432,6 +496,11 @@ def test_defaultifacelinuxroutefile_samples( def test_defaultifacelinuxroutefile(mocker): + # Test the test function + mocker.patch("getmac.utils.check_path", return_value=False) + assert getmac.DefaultIfaceLinuxRouteFile().test() is False + utils.check_path.assert_called_once_with("/proc/net/route") + mocker.patch("getmac.utils.read_file", return_value=None) assert getmac.DefaultIfaceLinuxRouteFile().get() is None @@ -488,8 +557,8 @@ def test_defaultifaceroutegetcommand_samples( mocker.patch("getmac.utils.popen", return_value="interface:") assert not getmac.DefaultIfaceRouteGetCommand().get() - mocker.patch("getmac.utils.check_command", return_value=True) - assert getmac.DefaultIfaceRouteGetCommand().test() is True + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.DefaultIfaceRouteGetCommand().test() is False utils.check_command.assert_called_once_with("route") @@ -555,6 +624,10 @@ def test_sys_iface_file(mocker): mocker.patch("getmac.utils.read_file", return_value=None) assert getmac.SysIfaceFile().get("ens33") is None + mocker.patch("getmac.utils.check_path", return_value=False) + assert getmac.SysIfaceFile().test() is False + utils.check_path.assert_called_once_with("/sys/class/net/") + @pytest.mark.skipif( platform.system() != "Linux", @@ -597,3 +670,13 @@ def test_lanscan_iface_samples(benchmark, mocker, get_sample, mac, iface, sample assert not getmac.LanscanIface().get("lan") assert not getmac.LanscanIface().get("lan100") assert not getmac.LanscanIface().get("lan90") + + +def test_lanscan_iface_edge_cases(mocker): + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.LanscanIface().test() is False + utils.check_command.assert_called_once_with("lanscan") + + mocker.patch("getmac.utils.popen", return_value="") + assert not getmac.LanscanIface().get("lan0") + utils.popen.assert_called_once_with("lanscan", "-ai") From 143d91b47bf7011166812208b524ad79ab66ef35 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 30 Dec 2025 14:35:29 -0700 Subject: [PATCH 108/112] fix!: raise RuntimeError in critical cases If no methods are found or all methods found fail to test, raise RuntimError exception with details. Previously, this would result in a RuntimeWarning, but not a raised exception. --- CHANGELOG.md | 3 ++- README.md | 2 +- docs/misc_docs/TODO.md | 5 +---- docs/usage.rst | 12 ++++++------ getmac/getmac.py | 35 ++++++++++++++--------------------- tests/test_getmac.py | 8 ++++---- 6 files changed, 28 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d44b88..f9264d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,8 @@ * Added platform detection for HP-UX and a default to `lan0` interface ### Changed -* BREAKING CHANGE: refactored how settings are handled. Instead of module-level globals, they're implemented in a Settings singleton in `getmac.settings`. For example, `getmac.getmac.PORT` should now be `getmac.settings.PORT`. +* **BREAKING CHANGE**: refactored how settings are handled. Instead of module-level globals, they're implemented in a Settings singleton in `getmac.settings`. For example, `getmac.getmac.PORT` should now be `getmac.settings.PORT`. +* **BREAKING CHANGE**: `RuntimeError` is now raised in cases where no methods are found matching the type of request and platform, or if all matching methods fail to test. This should almost never happen unless you're on an exotic platform or have an unusual configuration, or a recent platform update changed commands such that getmac no longer functions. If you encounter a `RuntimeError` exception, it means something went horribly wrong, and you should considor reporting the [issue on GitHub](https://github.com/GhostofGoes/getmac/issues). * Reduce size of wheel distribution (`.whl` file) ### Removed diff --git a/README.md b/README.md index cd67651..ed19393 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost - "Remote hosts" refer to hosts in your local layer 2 network, also commonly referred to as a "broadcast domain", "LAN", or "VLAN". As far as I know, there is not a reliable method to get a MAC address for a remote host external to the LAN. If you know any methods otherwise, please [open a GitHub issue](https://github.com/GhostofGoes/getmac/issues) or shoot me an email, I'd love to be wrong about this. - The first four arguments are mutually exclusive. `network_request` does not have any functionality when the `interface` argument is specified, and can be safely set if using in a script. - The physical transport is assumed to be Ethernet (802.3). Others, such as Wi-Fi (802.11), are currently not tested or considered. I plan to address this in the future, and am definitely open to pull requests or issues related to this, including error reports. -- **Exceptions will be handled silently and returned as a None.** If you run into problems, you can set `DEBUG` to true and get more information about what's happening. If you're still having issues, please create an [issue on GitHub](https://github.com/GhostofGoes/getmac/issues) and include the output with `DEBUG` enabled. +- **Most Exceptions will be handled silently and returned as a None.** If you run into problems, you can set `DEBUG` to true and get more information about what's happening. If you're still having issues, please create an [issue on GitHub](https://github.com/GhostofGoes/getmac/issues) and include the output with `DEBUG` enabled. As of 1.0.0, a `RuntimeError` is raised in cases where no possible methods are found matching the type of request and platform or all methods found fail to test (in other words, don't work). ## Commands and techniques by platform - Windows diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index f779aae..09ce49c 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -16,9 +16,6 @@ ## Tests - [ ] Add test to ensure only the expected files make it into the sdist and wheel, no unexpected files - [ ] >90% test coverage - - refactor tests to use the new system and structure - - directly test methods via a `Method.parse()` function - - add `Method.parse()` that handles the parsing of command - [ ] Improve CLI tests to ensure output is what's expected (e.g. ensure `--override-port` logs a warning and the value actually gets overridden) - [ ] Add tests for more samples (new third-party samples) @@ -28,7 +25,7 @@ ## Breaking changes (or potentially breaking) - [x] Replace the `UuidArpGetNode` method. It calls 3 commands and is quite inefficient. It's functionality is already implemented by `ArpVariousArgs`. -- [ ] Raise exceptions on critical failures (stuff that were warnings in 0.9.0), all calls to `_warn_critical()`. +- [x] Raise exceptions on critical failures (stuff that were warnings in 0.9.0), all calls to `_warn_critical()`. - [ ] **Consolidate `ip6` argument into `ip` argument.**. Parse based on `::` character vs `.` character if `str` or via `.version == 4`/`.version == 6` for `ipaddress` objects. - Combine `--ip` and `--ip6` CLI arguments into `--ip` output. this would make it *much* easier to test methods. - keep `-4,`, `-6`, and `--ip6` arguments for backwards-compatibility until 1.1.0 diff --git a/docs/usage.rst b/docs/usage.rst index 25d3d93..aa3e262 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -109,7 +109,7 @@ You can also use the standard library's :mod:`ipaddress` module to specify IP ad Default interface name ---------------------- -Get the name of the system's default network interface using :func:`~getmac.getmac.get_default_interface`. This leverages the same logic that getmac uses internally to determine the default interface (e.g. for when ``get_mac_address()`` is called without any arguments). +Get the name of the system's default network interface using :func:`~getmac.getmac.get_default_interface`. This leverages the same logic that getmac uses internally to determine the default interface (e.g. for when :func:`~getmac.getmac.get_mac_address` is called without any arguments). .. warning:: This currently doesn't work on Windows platforms. @@ -132,23 +132,23 @@ Settings that affect the behavior of getmac are in the :mod:`~getmac.variables` - :attr:`~getmac.variables.Settings.DEBUG`: integer value that controls debugging output. The higher the value, the more output you get. - :attr:`~getmac.variables.Settings.PORT`: the UDP port used to populate the ARP table (IPv4) or NDP list (IPv6) when looking up MACs for hosts or IPs (see the documentation of the ``network_request`` argument in :func:`~getmac.getmac.get_mac_address` for details). - :attr:`~getmac.variables.Settings.OVERRIDE_PLATFORM`: Override the platform detection with the given value (e.g. ``"linux"``, ``"windows"``, ``"freebsd"``, etc). Any values returned by :func:`platform.system` are valid. -- :attr:`~getmac.variables.Settings.FORCE_METHOD`: Name of method to use. This will force a specific method to be used, e.g. :class:`getmac.getmac.IpNeighborShow` with the string ``"IpNeighborShow"``. This will be used regardless of the method's type or platform compatibility, and :func:`Method.test() ` will NOT be checked! The list of available methods is in :data:`getmac.getmac.METHODS`. +- :attr:`~getmac.variables.Settings.FORCE_METHOD`: Name of method to use. This will force a specific method to be used, e.g. :class:`getmac.getmac.IpNeighborShow` with the string ``"IpNeighborShow"``. This will be used regardless of the method's type or platform compatibility, and :func:`~getmac.getmac.Method.test` will NOT be checked! The list of available methods is in :data:`getmac.getmac.METHODS`. Notes ===== - ``"localhost"`` or ``"127.0.0.1"`` will always return ``"00:00:00:00:00:00"``, as this is the MAC address of the loopback interface. -- If none of the arguments are selected, the default network interface for the system will be used. If the default network interface cannot be determined, then it will attempt to fallback to typical defaults for the platform (`Ethernet` on Windows, `em0` on BSD, `en0` on OSX/Darwin, and `eth0` otherwise). If that fails, then it will fallback to `lo` on POSIX systems. -- The first four arguments are mutually exclusive. `network_request` does not have any functionality when the `interface` argument is specified, and can be safely set if using in a script. -- **Exceptions will be handled silently and returned as a None.** If you run into problems, you can set `DEBUG` to true and get more information about what's happening. If you're still having issues, please create an [issue on GitHub](https://github.com/GhostofGoes/getmac/issues) and include the output with `DEBUG` enabled. +- If none of the arguments are selected, the default network interface for the system will be used. If the default network interface cannot be determined, then it will attempt to fallback to typical defaults for the platform (``Ethernet`` on Windows, ``em0`` on BSD, ``en0`` on OSX/Darwin, and ``eth0`` otherwise). If that fails, then it will fallback to ``lo`` on POSIX systems. +- The first four arguments to :func:`~getmac.getmac.get_mac_address` are mutually exclusive. ``network_request`` does not have any functionality when the ``interface`` argument is specified, and can be safely set if using in a script. +- **Most Exceptions will be handled silently and returned as a None.** If you run into problems, you can set :attr:`~getmac.variables.Settings.DEBUG` to true and get more information about what's happening. If you're still having issues, please create an `issue on GitHub `_ and include the output with :attr:`~getmac.variables.Settings.DEBUG` enabled. As of version 1.0.0, a :class:`RuntimeError` is raised in cases where no possible methods are found matching the type of request and platform or all methods found fail to test (in other words, don't work). Limitations & Known Issues ========================== - The physical transport is assumed to be Ethernet (802.3). Others, such as Wi-Fi (802.11), are currently not tested or considered. I plan to address this in the future, and am definitely open to pull requests or issues related to this, including error reports. -- "Remote hosts" refer to hosts in *your local layer 2 network*, also known as a "broadcast domain", "LAN", or "VLAN". As far as I know, there is not a reliable method to get a MAC address for a remote host external to the LAN. If you know any methods otherwise, please [open a GitHub issue](https://github.com/GhostofGoes/getmac/issues) or shoot me an email, I'd love to be wrong about this. +- "Remote hosts" refer to hosts in *your local layer 2 network*, also known as a "broadcast domain", "LAN", or "VLAN". As far as I know, there is not a reliable method to get a MAC address for a remote host external to the LAN. If you know any methods otherwise, please `a issue on GitHub `_ or shoot me an email, I'd love to be wrong about this. - Some methods may not work on certain platforms or configurations. If a method fails, getmac will try the next available method until it finds one that works or exhausts all options. - The accuracy and reliability of the MAC address retrieval may vary depending on the network configuration, the target host's settings, and other factors. Please refer to the documentation of each method for more details on their behavior and limitations. - Depending on the platform, there could be a performance detriment, due to heavy usage of regular expressions. diff --git a/getmac/getmac.py b/getmac/getmac.py index c9d9980..a9f833b 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -1226,14 +1226,6 @@ def _swap_method_fallback(method_type: str, swap_with: str) -> bool: return True -def _warn_critical(err_msg: str) -> None: - gvars.log.critical(err_msg) - warnings.warn( # noqa: B028 - f"{err_msg}. NOTICE: this warning my turn into a raised exception in a future release", - RuntimeWarning, - ) - - def initialize_method_cache(method_type: str, network_request: bool = True) -> bool: """ Initialize the method cache for the given method type. @@ -1246,6 +1238,11 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b Returns: If the cache was initialized successfully + + Raises: + RuntimeError: if no valid methods were found for the given method type + and the system's platform (or user-defined override platform), or + if all matching methods failed to test. """ if METHOD_CACHE.get(method_type): if settings.DEBUG: @@ -1282,8 +1279,7 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b ] # type: List[Type[Method]] if not type_methods: - _warn_critical(f"No valid methods matching MAC type '{method_type}'") - return False + raise RuntimeError(f"No valid methods matching MAC type '{method_type}'") if settings.DEBUG >= 2: type_strs = ", ".join(tm.__name__ for tm in type_methods) @@ -1318,10 +1314,9 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b ) if not platform_methods: - _warn_critical( + raise RuntimeError( f"No valid methods found for MAC type '{method_type}' and platform '{platform}'" ) - return False filtered_methods = platform_methods # type: List[Type[Method]] @@ -1347,10 +1342,9 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b gvars.log.debug(f"Test failed for method '{method_instance!s}'") if not tested_methods: - _warn_critical( + raise RuntimeError( f"All {len(filtered_methods)} '{method_type}' methods failed to test!" ) - return False if settings.DEBUG >= 2: tested_strs = ", ".join(str(ts) for ts in tested_methods) @@ -1396,8 +1390,7 @@ def _attempt_method_get(method: Method, method_type: str, arg: str) -> Optional[ Attempt to use methods, and if they fail, fallback to the next method in the cache. """ if not METHOD_CACHE[method_type] and not FALLBACK_CACHE[method_type]: - _warn_critical(f"No usable methods found for MAC type '{method_type}'") - return None + raise RuntimeError(f"No usable methods found for MAC type '{method_type}'") if settings.DEBUG: gvars.log.debug( @@ -1528,10 +1521,6 @@ def get_mac_address( The MAC is usually a unicast IEEE 802 MAC-48 address. - .. warning:: - In getmac 1.0.0, exceptions will be raised if there are no valid methods - found for the type of MAC requested. - .. note:: ``"localhost"`` or ``"127.0.0.1"`` will always return ``"00:00:00:00:00:00"`` @@ -1541,7 +1530,7 @@ def get_mac_address( relied upon. If this functionality is needed, please open an issue or PR. .. note:: - Exceptions raised by methods are handled silently and returned as :obj:`None`. + Exceptions raised by *methods* are handled silently and returned as :obj:`None`. Args: interface: Name of a local network interface (e.g "Ethernet 3", "eth0", "ens32") @@ -1564,6 +1553,10 @@ def get_mac_address( Returns: Lowercase colon-separated MAC address. If no MAC was found, or an exception occurred, :obj:`None` is returned. + + Raises: + RuntimeError: If no valid methods are found for the type of MAC requested, + or another critical error occurs (potentially due to a bug in getmac). """ # If debugging, start the timer diff --git a/tests/test_getmac.py b/tests/test_getmac.py index 3f45942..ddc43f0 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -111,10 +111,10 @@ def test_initialize_method_cache_bad_type(mocker): mocker.patch("getmac.getmac.FALLBACK_CACHE", {}) mocker.patch.object(consts, "PLATFORM", "linux") - with pytest.warns(RuntimeWarning): - assert not getmac.initialize_method_cache("invalid_method_type") - with pytest.warns(RuntimeWarning): - assert not getmac.initialize_method_cache("ip") + with pytest.raises(RuntimeError): + getmac.initialize_method_cache("invalid_method_type") + with pytest.raises(RuntimeError): + getmac.initialize_method_cache("ip") def test_initialize_method_cache_platform_override(mocker): From cb8774898c506c5bbd9e9fbd8fa85a87a3b5eded Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 31 Dec 2025 17:19:30 -0700 Subject: [PATCH 109/112] dev: switch to Ruff format This replaces black and isort --- CHANGELOG.md | 9 ++- CONTRIBUTING.md | 4 +- README.md | 2 - SECURITY.md | 8 +-- docs/misc_docs/TODO.md | 5 +- getmac/__main__.py | 30 ++++++---- getmac/getmac.py | 68 +++++++---------------- getmac/utils.py | 8 +-- getmac/variables.py | 12 +--- pdm.lock | 96 ++------------------------------ pyproject.toml | 23 ++++---- tests/test_cli.py | 4 +- tests/test_getmac.py | 23 ++++---- tests/test_methods.py | 122 ++++++++++++++++++++++++++++++----------- 14 files changed, 172 insertions(+), 242 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9264d8..ea4b1ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,20 +31,19 @@ ### Removed * Removed support for Python 2.7 - 3.7. Most of the tooling used by getmac no longer works with 3.7 and older. If you need to use one of these versions, pin to `getmac<1.0.0`. * Removed support for Jython. As of Dec 2025, [Jython](https://github.com/jython/jython) still does not support Python 3. If and when it supports Python 3, I'll re-add support for it. -* Removed explicit support for IronPython. [IronPython3 exists](https://github.com/IronLanguages/ironpython3), however I don't have a way to test it in CI. If someone knows of a way to test it in GitHub actions, let me know, and I'm happy to re-add explicit support. -* Removed RPM packaging. This wasn't being used anywhere, and hasn't been updated since version 0.6.0. Way back when, I dreamt of putting this in OS package repositories. However, I've now learned that's up to the maintainers of those repos (e.g., Ubuntu or Fedora maintainers), not me. +* Removed support for IronPython. [IronPython3 exists](https://github.com/IronLanguages/ironpython3), however I don't have a way to test it in CI. If someone knows of a way to test it in GitHub actions, let me know, and I'm happy to re-add support. +* Removed RPM packaging, as it hasn't been maintained since 0.6.0. * Removed `UuidArpGetnode` method. It's quite slow (performs up to 3 subprocess calls internally) and the functionality is already implemented by `ArpVariousArgs`. * Removed `UuidLanscan` method. This has been replaced with the new `LanscanIface` method. Some UUID internal function names changed with Python 3.9, and it made more sense to implement from scratch in getmac instead of continuing to rely on CPython-specific private functions. ### Dev -* Renamed `master` branch to `main` * Switched to [PDM](https://pdm-project.org) for project management - * Add `pyproject.toml`, consolidated most tool configurations here +* Added `pyproject.toml` and consolidated the configurations for most tools * Removed `setup.py`, `MANIFEST.in`, `requirements*.txt`, `tox.ini` * Switched to CodeCov from Coveralls -* Use [isort](https://pycqa.github.io/isort/index.html) to sort imports * Refactored source code documentation and added an API reference to the docs +* Use [Ruff's formatter](https://docs.astral.sh/ruff/formatter/) instead of Black and isort ## 0.9.5 (07/15/2024) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f47d299..7b0fd46 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,12 +17,12 @@ Your code *must*: * Pass CI (GitHub Actions) * Adhere to the [Code of Conduct](CODE_OF_CONDUCT.md) -Most of these requirements are checked in CI (GitHub Actions), including Python versions and most supported platforms. Code is formatted with [Black](https://github.com/psf/black). You can write whatever format you want, as long as you run Black (`pdm run format`) before pushing, you're good. +Most of these requirements are checked in CI (GitHub Actions), including Python versions and most supported platforms. Code is formatted with [Ruff's formatter](https://docs.astral.sh/ruff/formatter/). You can write whatever format you want, as long as you run the formatter before pushing, you're good (`pdm run format`). Please be respectful and follow the [Code of Conduct](CODE_OF_CONDUCT.md). Memes, references, and jokes are OK. Be nice, we're all human, and code is the great equalizer. ## Checklist before submitting a pull request -* [ ] Code is formatted using `black` (`pdm run format`) +* [ ] Code is formatted using `ruff` (`pdm run format`) * [ ] All tests run and pass locally * [ ] Tests: `pdm run test` * [ ] Benchmarks: `pdm run benchmark` diff --git a/README.md b/README.md index ed19393..b1abb47 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,6 @@ [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9388/badge)](https://www.bestpractices.dev/projects/9388) [![Weekly PyPI downloads](https://pepy.tech/badge/getmac/week)](https://pepy.tech/project/getmac) [![PyPI downloads](https://pepy.tech/badge/getmac)](https://pepy.tech/project/getmac) - -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![PDM-managed](https://img.shields.io/endpoint?url=https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2Fpdm-project%2F.github%2Fbadge.json)](https://pdm-project.org) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) diff --git a/SECURITY.md b/SECURITY.md index 0b02f73..d59d405 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,10 +4,10 @@ | Version | Supported | | ---------- | ------------------ | -| 1.0.x | :white_check_mark: Yes | -| 0.9.x | :heavy_exclamation_mark: Maintenance mode, security or major bug fixes only | -| 0.8.x | :x: No longer supported | | -| <= 0.7.x | :x: No longer supported | +| 1.0.x | Yes | +| 0.9.x | Maintenance mode, security or major bug fixes only | +| 0.8.x | No longer supported | +| <= 0.7.x | No longer supported | ## Reporting a Vulnerability or other security issue diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index 09ce49c..a1a4e3e 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -62,10 +62,8 @@ methods (list): Optional list of methods to use for MAC address lookup. - Fix is to check that the flag != 0x0, which should do the trick, unless there's an edge case that it misses. ## Before releasing -- [ ] Add a deprecation warning to `get-mac` package, don't publish it for 1.0.0 - [x] Update supported versions table in [SECURITY.md](../SECURITY.md) -- [ ] Automate publishing to PyPI+GitHub Release in GitHub Actions -- [ ] Edit README.md to append CHANGELOG before publishing to PyPI +- [ ] Automate publishing in GitHub Actions. When a tag is created, publish release to PyPI, and generate a GitHub release. ## Notes Removing Tox, maybe. Can do version enumeration in GitHub Actions, for all platforms. No need for me to do locally. @@ -190,6 +188,7 @@ This is going to be a bit more complicated since the highest metric routes are g # Dev +- [ ] OpenSSF best practices badge - [ ] Add typing stubs to [typeshed](https://github.com/python/typeshed) once getmac 1.0.0 is released ([guide](https://github.com/python/typeshed/blob/master/CONTRIBUTING.md)) - [ ] Add to Conda Forge ([example here](https://github.com/conda-forge/staged-recipes/pull/26828/files)) - [x] Add [isort](https://pycqa.github.io/isort/) (requires python 3.8+) diff --git a/getmac/__main__.py b/getmac/__main__.py index 2852524..332fb58 100644 --- a/getmac/__main__.py +++ b/getmac/__main__.py @@ -13,9 +13,7 @@ def build_parser() -> argparse.ArgumentParser: prog="getmac", description="Get MAC addresses of network interfaces or LAN hosts", ) - parser.add_argument( - "--version", action="version", version=f"getmac {getmac.__version__}" - ) + parser.add_argument("--version", action="version", version=f"getmac {getmac.__version__}") group = parser.add_mutually_exclusive_group(required=False) group.add_argument( @@ -26,13 +24,25 @@ def build_parser() -> argparse.ArgumentParser: help="Name of a network interface on the system", ) group.add_argument( - "-4", "--ip", type=str, default=None, help="IPv4 address of a remote host" + "-4", + "--ip", + type=str, + default=None, + help="IPv4 address of a remote host", ) group.add_argument( - "-6", "--ip6", type=str, default=None, help="IPv6 address of a remote host" + "-6", + "--ip6", + type=str, + default=None, + help="IPv6 address of a remote host", ) group.add_argument( - "-n", "--hostname", type=str, default=None, help="Hostname of a remote host" + "-n", + "--hostname", + type=str, + default=None, + help="Hostname of a remote host", ) parser.add_argument( @@ -93,7 +103,9 @@ def main() -> None: if args.debug or args.verbose: logging.basicConfig( - format="%(levelname)-8s %(message)s", level=logging.DEBUG, stream=sys.stderr + format="%(levelname)-8s %(message)s", + level=logging.DEBUG, + stream=sys.stderr, ) if args.debug: @@ -101,9 +113,7 @@ def main() -> None: if args.override_port: port = int(args.override_port) - gvars.log.debug( - "Using UDP port %d (overriding the default port %d)", port, settings.PORT - ) + gvars.log.debug(f"Using UDP port {port} (overriding the default port {settings.PORT})") settings.PORT = port if args.override_platform: diff --git a/getmac/getmac.py b/getmac/getmac.py index a9f833b..26c88cb 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -12,6 +12,7 @@ :caption: Examples from getmac import get_mac_address + eth_mac = get_mac_address(interface="eth0") win_mac = get_mac_address(interface="Ethernet 3") ip_mac = get_mac_address(ip="192.168.0.1") @@ -463,9 +464,7 @@ def get(self, arg: str) -> Optional[str]: try: # NOTE: the space prevents accidental matching of partial IPs - return ( - output.partition(arg + " ")[2].partition("lladdr")[2].strip().split()[0] - ) + return output.partition(arg + " ")[2].partition("lladdr")[2].strip().split()[0] except IndexError as ex: gvars.log.debug(f"IpNeighborShow failed with exception: {ex}") return None @@ -620,9 +619,7 @@ class IpconfigExe(Method): platforms = {"windows"} method_type = "iface" - _regex: Final[str] = ( - r"(?:\n?[^\n]*){1,8}Physical Address[ .:]+" + consts.MAC_RE_DASH + r"\r\n" - ) + _regex: Final[str] = r"(?:\n?[^\n]*){1,8}Physical Address[ .:]+" + consts.MAC_RE_DASH + r"\r\n" def test(self) -> bool: return utils.check_command("ipconfig.exe") @@ -880,9 +877,7 @@ def get(self, arg: str) -> Optional[str]: if self._working_regex: # Use regex that worked previously. This can still return None in # the case of interface not existing, but at least it's a bit faster. - return utils.search( - arg + self._working_regex, command_output, flags=re.DOTALL - ) + return utils.search(arg + self._working_regex, command_output, flags=re.DOTALL) # See if either regex matches for regex in self._regexes: @@ -1019,7 +1014,8 @@ def get(self, arg: str = "") -> Optional[str]: .partition("\n")[0] .split()[-1] ) - except IndexError as ex: # index errors means no default route in output? + except IndexError as ex: + # index errors means no default route in output? gvars.log.debug(f"DefaultIfaceRouteCommand failed for {arg}: {ex}") return None @@ -1246,14 +1242,10 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b """ if METHOD_CACHE.get(method_type): if settings.DEBUG: - gvars.log.debug( - f"Method cache already initialized for method type '{method_type}'" - ) + gvars.log.debug(f"Method cache already initialized for method type '{method_type}'") return True - gvars.log.debug( - f"Initializing '{method_type}' method cache (platform: '{consts.PLATFORM}')" - ) + gvars.log.debug(f"Initializing '{method_type}' method cache (platform: '{consts.PLATFORM}')") if settings.OVERRIDE_PLATFORM: gvars.log.warning( @@ -1288,9 +1280,7 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b ) # Filter methods by the platform we're running on - platform_methods = [ - method for method in type_methods if platform in method.platforms - ] # type: List[Type[Method]] + platform_methods = [method for method in type_methods if platform in method.platforms] # type: List[Type[Method]] if not platform_methods: # If there isn't a method for the current platform, @@ -1302,9 +1292,7 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b ) gvars.log.warning(warn_msg) warnings.warn(warn_msg, RuntimeWarning, stacklevel=2) - platform_methods = [ - method for method in type_methods if "other" in method.platforms - ] + platform_methods = [method for method in type_methods if "other" in method.platforms] if settings.DEBUG >= 2: plat_strs = ", ".join(pm.__name__ for pm in platform_methods) @@ -1342,15 +1330,11 @@ def initialize_method_cache(method_type: str, network_request: bool = True) -> b gvars.log.debug(f"Test failed for method '{method_instance!s}'") if not tested_methods: - raise RuntimeError( - f"All {len(filtered_methods)} '{method_type}' methods failed to test!" - ) + raise RuntimeError(f"All {len(filtered_methods)} '{method_type}' methods failed to test!") if settings.DEBUG >= 2: tested_strs = ", ".join(str(ts) for ts in tested_methods) - gvars.log.debug( - f"{len(tested_methods)} tested methods for '{method_type}': {tested_strs}" - ) + gvars.log.debug(f"{len(tested_methods)} tested methods for '{method_type}': {tested_strs}") # Populate fallback cache with all the tested methods, minus the currently active method if METHOD_CACHE[method_type] and METHOD_CACHE[method_type] in tested_methods: @@ -1435,9 +1419,7 @@ def _attempt_method_get(method: Method, method_type: str, arg: str) -> Optional[ return result -def get_by_method( - method_type: str, arg: str = "", network_request: bool = True -) -> Optional[str]: +def get_by_method(method_type: str, arg: str = "", network_request: bool = True) -> Optional[str]: """ Query for a MAC using a specific method. @@ -1464,9 +1446,7 @@ def get_by_method( forced_method = get_method_by_name(settings.FORCE_METHOD) if not forced_method: - gvars.log.error( - f"Invalid FORCE_METHOD method name '{settings.FORCE_METHOD}'" - ) + gvars.log.error(f"Invalid FORCE_METHOD method name '{settings.FORCE_METHOD}'") return None return forced_method().get(arg) @@ -1504,9 +1484,7 @@ def get_by_method( def get_mac_address( interface: Union[str, bytes, None] = None, - ip: Union[ - str, bytes, IPv4Address, IPv4Interface, IPv6Address, IPv6Interface, None - ] = None, + ip: Union[str, bytes, IPv4Address, IPv4Interface, IPv6Address, IPv6Interface, None] = None, ip6: Union[str, bytes, IPv6Address, IPv6Interface, None] = None, hostname: Union[str, bytes, None] = None, network_request: bool = True, @@ -1606,9 +1584,7 @@ def get_mac_address( "not a network. Try IPv6Address or IPv6Interface instead." ) else: - raise ValueError( - f"Unknown type for 'ip' argument: '{ip.__class__.__name__}'" - ) + raise ValueError(f"Unknown type for 'ip' argument: '{ip.__class__.__name__}'") if (hostname and hostname == "localhost") or (ip and ip == "127.0.0.1"): return "00:00:00:00:00:00" @@ -1629,8 +1605,7 @@ def get_mac_address( if not socket.has_ipv6: # TODO: raise exception instead of returning None? gvars.log.error( - "Cannot get the MAC address of a IPv6 host: " - "IPv6 is not supported on this system" + "Cannot get the MAC address of a IPv6 host: IPv6 is not supported on this system" ) return None @@ -1646,9 +1621,7 @@ def get_mac_address( "not a network. Try IPv6Address or IPv6Interface instead." ) elif not isinstance(ip6, str): - raise ValueError( - f"Unknown type for 'ip6' argument: '{ip6.__class__.__name__}'" - ) + raise ValueError(f"Unknown type for 'ip6' argument: '{ip6.__class__.__name__}'") if ":" not in ip6: gvars.log.error(f"Invalid IPv6 address (no ':'): {ip6}") @@ -1681,10 +1654,7 @@ def get_mac_address( if not mac: for arp_meth in ["CtypesHost", "ArpingHost"]: - if ( - settings.FORCE_METHOD - and settings.FORCE_METHOD.lower() != arp_meth - ): + if settings.FORCE_METHOD and settings.FORCE_METHOD.lower() != arp_meth: continue if arp_meth == str(METHOD_CACHE["ip4"]): diff --git a/getmac/utils.py b/getmac/utils.py index 33506f3..c652244 100644 --- a/getmac/utils.py +++ b/getmac/utils.py @@ -27,9 +27,7 @@ def check_command(command: str) -> bool: If the command exists """ if command not in gvars.CHECK_COMMAND_CACHE: - gvars.CHECK_COMMAND_CACHE[command] = bool( - shutil.which(command, path=gvars.PATH_STR) - ) + gvars.CHECK_COMMAND_CACHE[command] = bool(shutil.which(command, path=gvars.PATH_STR)) return gvars.CHECK_COMMAND_CACHE[command] @@ -126,9 +124,7 @@ def read_file(filepath: str) -> Optional[str]: return None -def search( - regex: str, text: str, group_index: int = 0, flags: int = 0 -) -> Optional[str]: +def search(regex: str, text: str, group_index: int = 0, flags: int = 0) -> Optional[str]: """ Search for a regular expression in a string, and return the specified group. This is thin wrapper around :func:`re.search` with some error handling. diff --git a/getmac/variables.py b/getmac/variables.py index 4a3ce95..0648ab7 100644 --- a/getmac/variables.py +++ b/getmac/variables.py @@ -67,9 +67,7 @@ class Constants(VarsClass): """ WSL1: Final[bool] = ( - _SYST == "Linux" - and "Microsoft" in _UNAME.version - and "-WSL2" not in _UNAME.release + _SYST == "Linux" and "Microsoft" in _UNAME.version and "-WSL2" not in _UNAME.release ) """ Windows Subsystem for Linux (WSL) version 1. @@ -80,9 +78,7 @@ class Constants(VarsClass): """ WSL2: Final[bool] = ( - _SYST == "Linux" - and "Microsoft" not in _UNAME.version - and "-WSL2" in _UNAME.release + _SYST == "Linux" and "Microsoft" not in _UNAME.version and "-WSL2" in _UNAME.release ) """ Windows Subsystem for Linux (WSL) version 2. @@ -98,9 +94,7 @@ class Constants(VarsClass): :meta hide-value: """ - ANDROID: Final[bool] = ( - hasattr(sys, "getandroidapilevel") or "ANDROID_STORAGE" in os.environ - ) + ANDROID: Final[bool] = hasattr(sys, "getandroidapilevel") or "ANDROID_STORAGE" in os.environ """ .. note:: "Linux" methods apply to Android without modifications. diff --git a/pdm.lock b/pdm.lock index f903085..82326aa 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "docs", "lint", "test"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:2da92c01ac895d4f8ad1b998cd69b7d4225b25b05add2e752438520c72ca3938" +content_hash = "sha256:24daaff9ccccad500297e42c4a731c5629327b1a07b82fd90b364028705e8dac" [[metadata.targets]] requires_python = "~=3.8" @@ -53,46 +53,6 @@ files = [ {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, ] -[[package]] -name = "black" -version = "24.8.0" -requires_python = ">=3.8" -summary = "The uncompromising code formatter." -groups = ["lint"] -dependencies = [ - "click>=8.0.0", - "mypy-extensions>=0.4.3", - "packaging>=22.0", - "pathspec>=0.9.0", - "platformdirs>=2", - "tomli>=1.1.0; python_version < \"3.11\"", - "typing-extensions>=4.0.1; python_version < \"3.11\"", -] -files = [ - {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, - {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, - {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, - {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, - {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, - {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, - {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, - {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, - {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, - {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, - {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, - {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, - {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, - {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, - {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, - {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, - {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, - {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, - {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, - {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, - {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, - {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, -] - [[package]] name = "certifi" version = "2025.7.9" @@ -195,21 +155,6 @@ files = [ {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, ] -[[package]] -name = "click" -version = "8.1.8" -requires_python = ">=3.7" -summary = "Composable command line interface toolkit" -groups = ["lint"] -dependencies = [ - "colorama; platform_system == \"Windows\"", - "importlib-metadata; python_version < \"3.8\"", -] -files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, -] - [[package]] name = "codespell" version = "2.4.1" @@ -226,8 +171,8 @@ name = "colorama" version = "0.4.6" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Cross-platform colored terminal text." -groups = ["docs", "lint", "test"] -marker = "sys_platform == \"win32\" or platform_system == \"Windows\"" +groups = ["docs", "test"] +marker = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -521,17 +466,6 @@ files = [ {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] -[[package]] -name = "isort" -version = "5.13.2" -requires_python = ">=3.8.0" -summary = "A Python utility / library to sort Python imports." -groups = ["lint"] -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - [[package]] name = "jinja2" version = "3.1.6" @@ -748,34 +682,12 @@ name = "packaging" version = "25.0" requires_python = ">=3.8" summary = "Core utilities for Python packages" -groups = ["docs", "lint", "test"] +groups = ["docs", "test"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] -[[package]] -name = "pathspec" -version = "0.12.1" -requires_python = ">=3.8" -summary = "Utility library for gitignore style pattern matching of file paths." -groups = ["lint"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.6" -requires_python = ">=3.8" -summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -groups = ["lint"] -files = [ - {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, - {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, -] - [[package]] name = "pluggy" version = "1.5.0" diff --git a/pyproject.toml b/pyproject.toml index c5472a2..dfce746 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,9 +95,7 @@ lint = [ "tomli; python_version < \"3.12\"", "ruff", "vulture", - "black", "mypy<2.0.0,>=1.3.0", - "isort", ] docs = [ "furo; python_version >= \"3.10\"", @@ -147,8 +145,8 @@ cmd = "vulture --min-confidence 100 getmac tests" [tool.pdm.scripts.check_format] help = "Check code formatting is correct" composite = [ - "isort --check getmac tests", - "black --check getmac tests", + "ruff check --select I getmac tests", + "ruff format --check getmac tests", ] [tool.pdm.scripts.typing] @@ -168,8 +166,8 @@ composite = [ [tool.pdm.scripts.format] help = "Auto-format code to match requirements" composite = [ - "isort getmac tests", - "black getmac tests", + "ruff check --select I --fix getmac tests", + "ruff format getmac tests", ] [tool.pdm.scripts.benchmark] @@ -253,6 +251,7 @@ select = [ "B", # flake8-bugbear "A", # flake8-builtins "C4", # flake8-comprehensions + "I", # isort "DTZ", # flake8-datetimez "T10", # flake8-debugger "ISC", # flake8-implicit-str-concat @@ -273,9 +272,8 @@ select = [ "PLE", # Pylint - Errors "PLR", # Pylint - Refactor "PLW", # Pylint - Warnings - # "UP", # pyupgrade + "UP", # pyupgrade "RUF", # Ruff-specific rules - ] # Linting error codes to ignore @@ -293,10 +291,13 @@ ignore = [ "PLR0915", "PLR0912", "PLR2004", + "UP031", ] [tool.ruff.lint.per-file-ignores] -"tests/*.py" = ["ANN001", "ANN002", "ANN003", "ANN201", "ANN202", "S101", "S104", "S603", "D", "PLR"] +"tests/*.py" = ["ANN001", "ANN002", "ANN003", "ANN201", "ANN202", "S101", "S104", "S602", "S603", "D", "PLR"] -[tool.isort] -profile = "black" +[tool.ruff.format] +docstring-code-format = true +line-ending = "lf" +quote-style = "double" diff --git a/tests/test_cli.py b/tests/test_cli.py index bc70bc0..9c4a012 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -50,9 +50,7 @@ def test_cli_no_net(): def test_cli_override_port(): - assert run_cmd( - [*BASE_CMD, "-v", "-dd", "-4", "127.0.0.1", "--override-port", "44444"] - ) + assert run_cmd([*BASE_CMD, "-v", "-dd", "-4", "127.0.0.1", "--override-port", "44444"]) def test_cli_localhost(): diff --git a/tests/test_getmac.py b/tests/test_getmac.py index ddc43f0..b917936 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -93,7 +93,12 @@ def test_initialize_method_cache_valid_types(mocker, method_type): def test_initialize_method_cache_initialized(mocker): mocker.patch( "getmac.getmac.METHOD_CACHE", - {"ip4": getmac.ArpFile(), "ip6": None, "iface": None, "default_iface": None}, + { + "ip4": getmac.ArpFile(), + "ip6": None, + "iface": None, + "default_iface": None, + }, ) mocker.patch("getmac.getmac.FALLBACK_CACHE", {}) mocker.patch.object(consts, "PLATFORM", "linux") @@ -168,9 +173,7 @@ def test_get_by_method(mocker, get_sample): # ip6 content = get_sample("android_9/ip_neighbor.out") mocker.patch("getmac.utils.popen", return_value=content) - assert ( - getmac.get_by_method("ip6", "fe80::8c8f:aaff:fec9:d28b") == "8e:8f:aa:c9:d2:8b" - ) + assert getmac.get_by_method("ip6", "fe80::8c8f:aaff:fec9:d28b") == "8e:8f:aa:c9:d2:8b" # iface content = get_sample("windows_10/wmic_nic.out") @@ -259,9 +262,7 @@ def test_get_mac_address_ip(mocker): # IPv4Interface mocker.patch("getmac.getmac.get_by_method", return_value="00:01:02:04:00:66") - assert ( - getmac.get_mac_address(ip=IPv4Interface("192.0.2.66/24")) == "00:01:02:04:00:66" - ) + assert getmac.get_mac_address(ip=IPv4Interface("192.0.2.66/24")) == "00:01:02:04:00:66" getmac.get_by_method.assert_called_once_with("ip4", "192.0.2.66") # IPv6Address @@ -271,9 +272,7 @@ def test_get_mac_address_ip(mocker): # IPv6Interface mocker.patch("getmac.getmac.get_by_method", return_value="00:01:02:04:00:44") - assert ( - getmac.get_mac_address(ip=IPv6Interface("fe80::44/24")) == "00:01:02:04:00:44" - ) + assert getmac.get_mac_address(ip=IPv6Interface("fe80::44/24")) == "00:01:02:04:00:44" getmac.get_by_method.assert_called_once_with("ip6", "fe80::44") @@ -298,9 +297,7 @@ def test_get_mac_address_ip6(mocker): # IPv6Interface mocker.patch("getmac.getmac.get_by_method", return_value="00:01:02:04:00:22") - assert ( - getmac.get_mac_address(ip6=IPv6Interface("fe80::22/24")) == "00:01:02:04:00:22" - ) + assert getmac.get_mac_address(ip6=IPv6Interface("fe80::22/24")) == "00:01:02:04:00:22" getmac.get_by_method.assert_called_once_with("ip6", "fe80::22") diff --git a/tests/test_methods.py b/tests/test_methods.py index ba59f49..5d84325 100644 --- a/tests/test_methods.py +++ b/tests/test_methods.py @@ -5,7 +5,7 @@ import pytest from getmac import getmac, utils -from getmac.variables import consts, settings +from getmac.variables import consts # TODO: freebsd11/netstat_-ia.out # TODO: netstat_-ian_aix.out @@ -17,9 +17,7 @@ def test_darwinnetworksetupiface(benchmark, mocker, get_sample): content = get_sample("macos_10.12.6/networksetup_-getmacaddress_en0.out") mocker.patch("getmac.utils.popen", return_value=content) - assert "08:00:27:2b:c2:ed" == benchmark( - getmac.DarwinNetworksetupIface().get, arg="en0" - ) + assert "08:00:27:2b:c2:ed" == benchmark(getmac.DarwinNetworksetupIface().get, arg="en0") mocker.patch("getmac.utils.popen", return_value=None) assert not getmac.DarwinNetworksetupIface().get("en0") @@ -187,11 +185,11 @@ def test_arping_host_edge_cases(mocker): cmd="arping -f -c 1 192.0.2.1", output=b"invalid option", returncode=1 ) mocker.patch("getmac.utils.popen", side_effect=cpe) - mocker.patch.object(settings, "DEBUG", 1) # Standard case mocker.patch( - "getmac.getmac.ArpingHost._call_habets", return_value="00:50:56:e8:32:3c" + "getmac.getmac.ArpingHost._call_habets", + return_value="00:50:56:e8:32:3c", ) assert getmac.ArpingHost().get("192.168.16.254") == "00:50:56:e8:32:3c" @@ -294,14 +292,34 @@ def test_arpfreebsd_samples(benchmark, mocker, get_sample, mac, ip, sample_file) @pytest.mark.parametrize( ("mac", "ip", "sample_file"), [ - ("00:50:56:f1:4c:50", "192.168.16.2", "ubuntu_18.04/cat_proc-net-arp.out"), + ( + "00:50:56:f1:4c:50", + "192.168.16.2", + "ubuntu_18.04/cat_proc-net-arp.out", + ), ("00:50:56:e1:a8:4a", "192.168.16.2", "ubuntu_18.10/proc_net_arp.out"), - ("00:50:56:e8:32:3c", "192.168.16.254", "ubuntu_18.10/proc_net_arp.out"), + ( + "00:50:56:e8:32:3c", + "192.168.16.254", + "ubuntu_18.10/proc_net_arp.out", + ), ("00:50:56:c0:00:0a", "192.168.95.1", "ubuntu_18.10/proc_net_arp.out"), - ("00:50:56:fa:b7:54", "192.168.95.254", "ubuntu_18.10/proc_net_arp.out"), + ( + "00:50:56:fa:b7:54", + "192.168.95.254", + "ubuntu_18.10/proc_net_arp.out", + ), ("52:55:0a:00:02:02", "10.0.2.2", "android_6/cat_proc-net-arp.out"), - ("02:00:00:00:01:00", "192.168.232.1", "android_9/cat_proc-net-arp.out"), - ("8e:8f:aa:c9:d2:8b", "192.168.200.1", "android_9/cat_proc-net-arp.out"), + ( + "02:00:00:00:01:00", + "192.168.232.1", + "android_9/cat_proc-net-arp.out", + ), + ( + "8e:8f:aa:c9:d2:8b", + "192.168.200.1", + "android_9/cat_proc-net-arp.out", + ), ], ) def test_arpfile_samples(benchmark, mocker, get_sample, mac, ip, sample_file): @@ -329,12 +347,24 @@ def test_arpfile_samples(benchmark, mocker, get_sample, mac, ip, sample_file): "192.168.16.2", "ubuntu_18.04/ip_neighbor_show_192-168-16-2.out", ), - ("00:50:56:f1:4c:50", "192.168.16.2", "ubuntu_18.04/ip_neighbor_show.out"), - ("52:55:0a:00:02:02", "10.0.2.2", "android_6/ip_neighbor_show_10.0.2.2.out"), + ( + "00:50:56:f1:4c:50", + "192.168.16.2", + "ubuntu_18.04/ip_neighbor_show.out", + ), + ( + "52:55:0a:00:02:02", + "10.0.2.2", + "android_6/ip_neighbor_show_10.0.2.2.out", + ), ("52:55:0a:00:02:02", "10.0.2.2", "android_6/ip_neighbor.out"), ("52:56:00:00:00:02", "fe80::2", "android_6/ip_neighbor.out"), ("8e:8f:aa:c9:d2:8b", "192.168.200.1", "android_9/ip_neighbor.out"), - ("8e:8f:aa:c9:d2:8b", "fe80::8c8f:aaff:fec9:d28b", "android_9/ip_neighbor.out"), + ( + "8e:8f:aa:c9:d2:8b", + "fe80::8c8f:aaff:fec9:d28b", + "android_9/ip_neighbor.out", + ), ], ) def test_ipneighborshow_samples(benchmark, mocker, get_sample, mac, ip, sample_file): @@ -465,9 +495,7 @@ def test_ip_link_iface_edge_cases(mocker, get_sample): ("eth0", "android_6/route_-n.out"), ], ) -def test_default_iface_route_command( - benchmark, mocker, get_sample, expected_iface, sample_file -): +def test_default_iface_route_command(benchmark, mocker, get_sample, expected_iface, sample_file): content = get_sample(sample_file) mocker.patch("getmac.utils.popen", return_value=content) assert expected_iface == benchmark(getmac.DefaultIfaceRouteCommand().get) @@ -487,9 +515,7 @@ def test_default_iface_route_command( (None, "android_9/cat_proc-net-route.out"), ], ) -def test_defaultifacelinuxroutefile_samples( - benchmark, mocker, get_sample, iface, sample_file -): +def test_defaultifacelinuxroutefile_samples(benchmark, mocker, get_sample, iface, sample_file): content = get_sample(sample_file) mocker.patch("getmac.utils.read_file", return_value=content) assert benchmark(getmac.DefaultIfaceLinuxRouteFile().get) == iface @@ -540,9 +566,7 @@ def test_defaultifaceiproute(mocker): ("em0", "freebsd11/route_get_default.out"), ], ) -def test_defaultifaceroutegetcommand_samples( - benchmark, mocker, get_sample, iface, sample_file -): +def test_defaultifaceroutegetcommand_samples(benchmark, mocker, get_sample, iface, sample_file): content = get_sample(sample_file) mocker.patch("getmac.utils.popen", return_value=content) assert iface == benchmark(getmac.DefaultIfaceRouteGetCommand().get) @@ -602,14 +626,14 @@ def test_arp_various_args_samples(benchmark, mocker, get_sample, mac, ip, sample def test_arp_various_args_edge_cases(mocker, get_sample): assert not getmac.ArpVariousArgs().get("") - mocker.patch.object(settings, "DEBUG", 1) cpe = CalledProcessError(cmd="arp", returncode=1) mocker.patch("getmac.utils.popen", side_effect=cpe) assert getmac.ArpVariousArgs().get("192.0.2.1") is None # Not sure if IP is required on Ubuntu 18, this is just for purposes of testing mocker.patch( - "getmac.utils.popen", return_value=get_sample("ubuntu_18.04/arp_-an.out") + "getmac.utils.popen", + return_value=get_sample("ubuntu_18.04/arp_-an.out"), ) inst = getmac.ArpVariousArgs() inst._args_tested = True @@ -648,16 +672,48 @@ def test_fcntl_iface(mocker): ("mac", "iface", "sample_file"), [ # hpux: lanscan -iap - ("00:17:A4:77:08:A4", "lan1", "third_party/glpi_agent/hpux/lanscan/hpux"), - ("00:17:A4:77:08:A2", "lan6", "third_party/glpi_agent/hpux/lanscan/hpux"), - ("00:17:A4:77:08:EA", "snap31", "third_party/glpi_agent/hpux/lanscan/hpux"), - ("00:00:00:00:00:00", "lan901", "third_party/glpi_agent/hpux/lanscan/hpux"), - ("00:00:00:00:00:00", "lan904", "third_party/glpi_agent/hpux/lanscan/hpux"), + ( + "00:17:A4:77:08:A4", + "lan1", + "third_party/glpi_agent/hpux/lanscan/hpux", + ), + ( + "00:17:A4:77:08:A2", + "lan6", + "third_party/glpi_agent/hpux/lanscan/hpux", + ), + ( + "00:17:A4:77:08:EA", + "snap31", + "third_party/glpi_agent/hpux/lanscan/hpux", + ), + ( + "00:00:00:00:00:00", + "lan901", + "third_party/glpi_agent/hpux/lanscan/hpux", + ), + ( + "00:00:00:00:00:00", + "lan904", + "third_party/glpi_agent/hpux/lanscan/hpux", + ), # hpux1: lanscan -iap - ("00:16:35:3E:AC:5C", "lan0", "third_party/glpi_agent/hpux/lanscan/hpux1"), + ( + "00:16:35:3E:AC:5C", + "lan0", + "third_party/glpi_agent/hpux/lanscan/hpux1", + ), # hpux2: lanscan -iap - ("00:16:35:3E:AC:44", "lan0", "third_party/glpi_agent/hpux/lanscan/hpux2"), - ("00:16:35:3E:AC:45", "lan1", "third_party/glpi_agent/hpux/lanscan/hpux2"), + ( + "00:16:35:3E:AC:44", + "lan0", + "third_party/glpi_agent/hpux/lanscan/hpux2", + ), + ( + "00:16:35:3E:AC:45", + "lan1", + "third_party/glpi_agent/hpux/lanscan/hpux2", + ), ], ) def test_lanscan_iface_samples(benchmark, mocker, get_sample, mac, iface, sample_file): From eebad08b9410696da1844ecc4a9b99eb0f491006 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 31 Dec 2025 17:21:40 -0700 Subject: [PATCH 110/112] test: add test for packaging artifacts --- docs/misc_docs/TODO.md | 2 +- tests/test_packaging.py | 89 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 tests/test_packaging.py diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index a1a4e3e..fd36ae9 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -14,7 +14,7 @@ - [x] Furo, sphinx-autodoc-typehints, sphinx-argparse-cli, sphinx-automodapi, sphinx-copybutton, recommonmark ## Tests -- [ ] Add test to ensure only the expected files make it into the sdist and wheel, no unexpected files +- [x] Add test to ensure only the expected files make it into the sdist and wheel, no unexpected files - [ ] >90% test coverage - [ ] Improve CLI tests to ensure output is what's expected (e.g. ensure `--override-port` logs a warning and the value actually gets overridden) - [ ] Add tests for more samples (new third-party samples) diff --git a/tests/test_packaging.py b/tests/test_packaging.py new file mode 100644 index 0000000..99eec46 --- /dev/null +++ b/tests/test_packaging.py @@ -0,0 +1,89 @@ +""" +Tests for packaging artifacts: sdist and wheel. +This test was mostly generated using AI assistance. +""" + +import subprocess +import tarfile +import zipfile +from pathlib import Path + + +def test_packaging_artifacts(tmp_path: Path) -> None: + """Build sdist and wheel, then verify their contents.""" + dist_dir = tmp_path / "dist" + + # Build the package + subprocess.run( + f"pdm build --dest {dist_dir!s}", + check=True, + capture_output=True, + shell=True, + ) + + # 1. Check sdist + sdists = list(dist_dir.glob("*.tar.gz")) + assert len(sdists) == 1, "Expected exactly one sdist (.tar.gz)" + + with tarfile.open(sdists[0], "r:gz") as tar: + sdist_members = tar.getnames() + + # Normalize paths: remove the top-level directory (e.g. getmac-1.0.0/) + sdist_files = [] + for m in sdist_members: + parts = m.split("/", 1) + if len(parts) > 1: + sdist_files.append(parts[1]) + else: + sdist_files.append(m) + + # Forbidden files/directories that should never be in the distribution + forbidden_starts = [ + ".github", + ".git", + ".vscode", + ".idea", + "build", + "dist", + "venv", + ".venv", + ".pdm-python", + "__pycache__", + ] + + for f in sdist_files: + for bad in forbidden_starts: + assert not f.startswith(bad), f"File '{f}' should not be in sdist" + + # Essential files that MUST be in sdist + expected_in_sdist = [ + "pyproject.toml", + "README.md", + "LICENSE", + "CHANGELOG.md", + ] + for expected in expected_in_sdist: + assert expected in sdist_files, f"'{expected}' missing from sdist" + + # Ensure source code and tests are present in sdist + assert any(f.startswith("getmac/") for f in sdist_files), "Source code missing from sdist" + assert any(f.startswith("tests/") for f in sdist_files), "Tests missing from sdist" + + # 2. Check wheel + wheels = list(dist_dir.glob("*.whl")) + assert len(wheels) == 1, "Expected exactly one wheel (.whl)" + + with zipfile.ZipFile(wheels[0], "r") as z: + wheel_files = z.namelist() + + # Tests should NOT be in wheel (assuming standard practice for this lib) + for f in wheel_files: + assert not f.startswith("tests/"), f"File '{f}' (tests) should not be in wheel" + for bad in forbidden_starts: + assert not f.startswith(bad), f"File '{f}' should not be in wheel" + + # Ensure package is in wheel + assert any(f.startswith("getmac/") for f in wheel_files), "Source code missing from wheel" + assert any(f.endswith(".dist-info/METADATA") for f in wheel_files), ( + "Metadata missing from wheel" + ) From 63ca0ab1d8f0ce02210cb11233bbc6312dd49b1b Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 31 Dec 2025 17:45:41 -0700 Subject: [PATCH 111/112] dev: revisit auto-publishing later --- .github/workflows/ci.yml | 51 +++++++++++++++++++++++----------------- docs/misc_docs/TODO.md | 6 ++--- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c80672..3701a9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -126,24 +126,33 @@ jobs: # TODO: don't publish pre-releases OR publish them as pre-releases to PyPI # https://docs.pypi.org/trusted-publishers/adding-a-publisher/ # https://pdm-project.org/en/latest/usage/publish/ -# name: "Build and publish a release" -# on: -# release: -# types: [released] -# jobs: -# pypi-publish: -# name: Publish release to PyPI -# runs-on: "ubuntu-22.04" -# permissions: -# # This permission is needed for private repositories. -# contents: read -# # IMPORTANT: this permission is mandatory for trusted publishing -# id-token: write -# steps: -# - uses: actions/checkout@v6 -# - name: Setup PDM -# uses: pdm-project/setup-pdm@v4 -# with: -# python-version: "3.11" -# - name: Publish package distributions to PyPI -# run: pdm publish + # publish: + # name: Publish Release + # needs: [test, lint, docs] + # runs-on: ubuntu-latest + # if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push' + # permissions: + # contents: write + # id-token: write + # steps: + # - uses: actions/checkout@v6 + # - name: Setup PDM + # uses: pdm-project/setup-pdm@v4 + # with: + # python-version: "3.11" + # cache: true + # - name: Build artifacts + # run: pdm build + # - name: Publish to PyPI + # run: pdm publish + # - uses: actions/download-artifact@v6 + # with: + # name: manpage + # path: manpage_dist + # - name: Create GitHub Release + # uses: softprops/action-gh-release@v2 + # with: + # files: | + # dist/* + # manpage_dist/getmac.1 + # generate_release_notes: true diff --git a/docs/misc_docs/TODO.md b/docs/misc_docs/TODO.md index fd36ae9..0d308dc 100644 --- a/docs/misc_docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -63,10 +63,6 @@ methods (list): Optional list of methods to use for MAC address lookup. ## Before releasing - [x] Update supported versions table in [SECURITY.md](../SECURITY.md) -- [ ] Automate publishing in GitHub Actions. When a tag is created, publish release to PyPI, and generate a GitHub release. - -## Notes -Removing Tox, maybe. Can do version enumeration in GitHub Actions, for all platforms. No need for me to do locally. ## Done for 1.0.0 - [x] Move to PDM from Poetry @@ -194,6 +190,8 @@ This is going to be a bit more complicated since the highest metric routes are g - [x] Add [isort](https://pycqa.github.io/isort/) (requires python 3.8+) - [ ] Move method classes into a separate file - [ ] Create a script to collect samples for all relevant commands on a platform and save output into the appropriately named sub-directory in `samples/`. +- [ ] Automate publishing in GitHub Actions. When a tag is created, publish release to PyPI, and generate a GitHub release. + - This is going to require re-doing how changelogs are created a bit. # Post-1.0.0 From 3683508da46502621af09c2b1ab0a040a215b82b Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 3 Jan 2026 17:37:31 -0700 Subject: [PATCH 112/112] test: improve test coverage in get_mac_address --- tests/test_getmac.py | 90 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/tests/test_getmac.py b/tests/test_getmac.py index b917936..ea38ea0 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -336,6 +336,10 @@ def test_get_mac_address_default_args_fallback_global(mocker): def test_get_mac_address_invalid_types(): + """ + Test that invalid types for 'ip' and 'ip6' arguments raise ValueError. + """ + with pytest.raises(ValueError, match="IPv4Network"): getmac.get_mac_address(ip=IPv4Network("192.0.1.0/24")) @@ -352,6 +356,92 @@ def test_get_mac_address_invalid_types(): getmac.get_mac_address(ip6=object()) +def test_get_mac_address_default_interface(mocker): + """ + Test default interface is used when no other arguments are given. + """ + # need to mock get_by_method called with: + # "default_iface" => test_iface + # "iface", "test_iface" => MAC address + comp_mac = "00:11:22:33:44:55" + + def __test_iface_default(a1, a2=None): # noqa: ARG001 + if a1 == "default_iface": + return "test_iface" + return comp_mac + + mocker.patch.object(consts, "WINDOWS", False) + mocker.patch.object(gvars, "DEFAULT_IFACE", "") + mocker.patch( + "getmac.getmac.get_by_method", + side_effect=__test_iface_default, + ) + assert getmac.get_mac_address() == comp_mac + + +def test_get_mac_address_default_interface_fallback(mocker): + """ + More coverage of the fallback logic if default interface can't be determined. + """ + mocker.patch.object(consts, "WINDOWS", False) + comp_mac = "00:11:22:33:44:44" + + def __test_iface_fallback(a1, a2=None): # noqa: ARG001 + if a1 == "default_iface": + return "" + return comp_mac + + # BSD fallback path + mocker.patch.object(consts, "BSD", True) + mocker.patch.object(gvars, "DEFAULT_IFACE", "") + mocker.patch( + "getmac.getmac.get_by_method", + side_effect=__test_iface_fallback, + ) + assert getmac.get_mac_address() == comp_mac + assert gvars.DEFAULT_IFACE == "em0" + + # Darwin fallback path + mocker.patch.object(consts, "BSD", False) + mocker.patch.object(consts, "DARWIN", True) + mocker.patch.object(gvars, "DEFAULT_IFACE", "") + mocker.patch( + "getmac.getmac.get_by_method", + side_effect=__test_iface_fallback, + ) + assert getmac.get_mac_address() == comp_mac + assert gvars.DEFAULT_IFACE == "en0" + + # HPUX fallback path + mocker.patch.object(consts, "DARWIN", False) + mocker.patch.object(consts, "HPUX", True) + mocker.patch.object(gvars, "DEFAULT_IFACE", "") + mocker.patch( + "getmac.getmac.get_by_method", + side_effect=__test_iface_fallback, + ) + assert getmac.get_mac_address() == comp_mac + assert gvars.DEFAULT_IFACE == "lan0" + + # eth0 fallback path + mocker.patch.object(consts, "HPUX", False) + mocker.patch.object(gvars, "DEFAULT_IFACE", "") + mocker.patch( + "getmac.getmac.get_by_method", + side_effect=__test_iface_fallback, + ) + assert getmac.get_mac_address() == comp_mac + assert gvars.DEFAULT_IFACE == "eth0" + + # test hack to fallback to loopback + mocker.patch.object(gvars, "DEFAULT_IFACE", "") + mocker.patch( + "getmac.getmac.get_by_method", + side_effect=lambda a1, a2=None: "00:11:22:33:44:04" if a2 == "lo" else "", # noqa: ARG005 + ) + assert getmac.get_mac_address() == "00:11:22:33:44:04" + + def test_get_default_interface(mocker, get_sample): mocker.patch( "getmac.getmac.METHOD_CACHE",