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/.editorconfig b/.editorconfig index f6051be..0ab935c 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] @@ -36,4 +42,13 @@ 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 + +[*.md] +insert_final_newline = true +trim_trailing_whitespace = true 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]. 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 3d166c1..3701a9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,34 +1,27 @@ 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-24.04 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v6 + - name: Setup PDM + uses: pdm-project/setup-pdm@v4 with: - python-version: "3.8" - - name: Install tox - run: python -m pip install tox + python-version: "3.11" + cache: true + - name: Install dependencies + # NOTE: test is needed here to evaluate imports for pytest + run: pdm sync -d -G lint,test - name: Run lint checks - run: tox -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: @@ -38,105 +31,128 @@ jobs: # OS images: https://github.com/actions/runner-images matrix: python-version: - - "3.7" - "3.8" - "3.9" - - "pypy-3.7" - - "pypy-3.8" - - "pypy-3.9" + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" architecture: - x86 - x64 + - arm64 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 - # TODO: update tests to use ARM MacOS with 14+, add - # architecture to the list of architectures above. - - macos-13 + - macos-latest exclude: - - platform: ubuntu-20.04 + - 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-13 - python-version: "pypy-3.7" - - platform: macos-13 - python-version: "pypy-3.8" + - platform: macos-latest + architecture: x64 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v6 + - name: Setup PDM + uses: pdm-project/setup-pdm@v4 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} - 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 - run: python -m pip install tox coveralls tox-gh-actions + run: pdm sync -d -G test # TODO: fix benchmark results (maybe a separate Job?) - name: Run tests - run: tox - - uses: codecov/codecov-action@v3 + run: pdm run test + - 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 - - name: Coveralls - if: ${{ matrix.platform == 'ubuntu-20.04' && github.event_name != 'pull_request' }} - uses: AndreMiras/coveralls-python-action@develop + token: ${{ secrets.CODECOV_TOKEN }} + docs: + name: Build HTML docs and 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 docs + run: pdm run docs + - uses: actions/upload-artifact@v6 + with: + name: html-docs + path: html_docs/ + - uses: actions/upload-pages-artifact@v4 with: - parallel: true - flag-name: ${{ matrix.python-version }}-${{ matrix.platform }} - coveralls_finish: - needs: test - runs-on: ubuntu-20.04 + 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: Coveralls Finished - if: ${{ github.event_name != 'pull_request' }} - uses: AndreMiras/coveralls-python-action@develop - with: - parallel-finished: true - + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 -# 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" -# 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" -# run: "python -m pip install wheel --user" -# - name: "🐍 Build a binary wheel and a source tarball" -# run: "python setup.py sdist bdist_wheel" -# - name: "📦 Publish distribution to PyPI" -# uses: "pypa/gh-action-pypi-publish@master" -# if: "startsWith(github.ref, 'refs/tags')" -# with: -# password: "${{ secrets.pypi_password }}" +# https://docs.pypi.org/trusted-publishers/adding-a-publisher/ +# https://pdm-project.org/en/latest/usage/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/.gitignore b/.gitignore index 254a628..741f83e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,12 @@ .idea/ .vscode/ .vagrant/ +.pdm-python + +built_docs/ +html_docs/ +manpage/ +docs/api/ .pytest_cache diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d9211f..ea4b1ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,49 @@ + # 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). -**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) + +### Added +* 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. + - 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 :) +* Added manpage for the command line interface (`getmac.1`) +* 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`. +* **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 +* 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 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 +* Switched to [PDM](https://pdm-project.org) for project management +* Added `pyproject.toml` and consolidated the configurations for most tools + * Removed `setup.py`, `MANIFEST.in`, `requirements*.txt`, `tox.ini` +* Switched to CodeCov from Coveralls +* 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) @@ -246,7 +287,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/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/CONTRIBUTING.md b/CONTRIBUTING.md index 04fb6e0..7b0fd46 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,95 +1,97 @@ # 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) -* 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 -* 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. +## Code requirements + +Your code *must*: +* Have tests +* 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 [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` (`black getmac tests`) +* [ ] Code is formatted using `ruff` (`pdm run format`) * [ ] All tests run and pass locally - * [ ] `tox` - * [ ] `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 +## 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 -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! +### 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 +### 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)) -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/`. +2. Install PDM: https://pdm-project.org/en/latest/#installation +3. Create local environment: ```bash - python -m pip install --user -U tox - python -m pip install -e . + pdm install -d ``` -5. Setup and run the tests: +4. Ensure tests and linting works: ```bash - # Run code quality checks - tox -e check - - # Run the tests - tox + pdm run lint + pdm run test ``` -6. Write some code! Git commit messages should information about what changed, -and if it's relevant, the rationale (thinking) for the change. +5. Write some code! Git commit messages should information about what changed, and if it's relevant, the rationale (thinking) for the change. +6. Format your code: + ```bash + pdm run format + ``` 7. Follow the checklist in the pull request template 8. Submit a pull request! -# Bug reports +## Bug reports Filing a bug report: 1. Answer these questions: @@ -102,20 +104,47 @@ 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). +## 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) - [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 +pdm install -d + +# List scripts, these can be run with "pdm run" +pdm run -l + +# Run tests +pdm run test + +# Lint checks +pdm run lint + +# Run getmac CLI +pdm run getmac --help +pdm run getmac --version +``` + +## 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 +``` 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 diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 57fe3df..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 .coveragerc -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 7aad003..b1abb47 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,16 @@ -[![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) +[![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 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) +[![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 + Pure-Python package to get the MAC address of network interfaces and hosts on the local network. @@ -14,9 +19,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()` - -[![asciicast](https://asciinema.org/a/rk6dUACUcZY18taCuIBE5Ssus.png)](https://asciinema.org/a/rk6dUACUcZY18taCuIBE5Ssus) +It provides one primary function: `get_mac_address()` [![asciicast](https://asciinema.org/a/n3insrxfyECch6wxtJEl3LHfv.png)](https://asciinema.org/a/n3insrxfyECch6wxtJEl3LHfv) @@ -29,21 +32,34 @@ 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 +`apt` 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 +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") @@ -52,18 +68,20 @@ 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)) + +# 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 -**Python 2 users**: use `getmac2` or `python -m getmac` instead of `getmac`. ```bash getmac --help getmac --version @@ -114,59 +132,50 @@ getmac --ip 192.168.0.1 --override-platform windows getmac -v -dddd --ip 192.168.0.1 --force-method ctypeshost ``` -## Function: 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 - `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 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.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/) - 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 3.2: get-mac 0.3.0 -- Python 3.3: get-mac 0.3.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. - 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 - 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` - - 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` - - Same commands as Linux + - Commands: `networksetup`, `arping` + - Many of the same commands as Linux - WSL - Windows commands are used for remote hosts - Unix commands are used for interfaces @@ -184,19 +193,35 @@ 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+) - 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` +- 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 :) ## 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. @@ -231,9 +256,9 @@ 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 +## Contributors - Christopher Goes (@ghostofgoes) - Author and maintainer - Calvin Tran (@cyberhobbes) - Windows interface detection improvements - Daniel Flanagan (@FlantasticDan) - Code cleanup @@ -258,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/SECURITY.md b/SECURITY.md index 3c8974a..d59d405 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 | 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/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/api.rst b/docs/api.rst new file mode 100644 index 0000000..fd6b18e --- /dev/null +++ b/docs/api.rst @@ -0,0 +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/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/cli.rst b/docs/cli.rst new file mode 100644 index 0000000..763ee4d --- /dev/null +++ b/docs/cli.rst @@ -0,0 +1,167 @@ +====================== +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 +=========== + +.. note:: + 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* 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 + + # Print usage and command line arguments + getmac --help + + # Print the current version + getmac --version + + # No arguments returns the 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 + + + +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. + +.. 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 + + + +.. _python-invoke: + +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, replace calls to ``getmac`` in the examples above with ``python3 -m getmac`` (or whatever invocation applies to your platform). + +.. tab:: Linux/macOS + + .. code-block:: shell + + python3 -m getmac + python3 -m getmac --help + python3 -m getmac --version + +.. tab:: Windows + + .. 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 + +.. 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 + + +.. _args: + +Arguments reference +=================== + +.. sphinx_argparse_cli:: + :module: getmac.__main__ + :func: build_parser 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 e8217b2..f22bc3e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,25 +1,8 @@ -# -*- coding: utf-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 ----------------------------------------------------- @@ -33,91 +16,46 @@ # -- 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.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: + 'myst_parser', # Markdown support ] -# 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 ------------------------------------------ # 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 = [ - (master_doc, 'getmac', 'Cross-platform Python package to get MAC addresses', + ('cli', '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/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 new file mode 100644 index 0000000..f6cc257 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,21 @@ +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 `. + +.. toctree:: + :hidden: + :titlesonly: + + installation + usage + cli + api + changelog + contributing + security + code_of_conduct 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/TODO.md b/docs/misc_docs/TODO.md similarity index 63% rename from docs/TODO.md rename to docs/misc_docs/TODO.md index 28ed81d..0d308dc 100644 --- a/docs/TODO.md +++ b/docs/misc_docs/TODO.md @@ -1,101 +1,97 @@ + # 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 -- [ ] Add guide on using the modules API, e.g. registering a new method in `getmac.getmac.METHODS`, etc. -- [ ] Switch to Poetry for project management - - [ ] Also, add `isort` -- [ ] Support Python 3.10 - - [ ] 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 + +## Documentation +- [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 +- [x] Document Method (and subclass) attributes (use Sphinx "#:" comments) +- [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 +- [x] Add docstrings to all util methods +- [x] Furo, sphinx-autodoc-typehints, sphinx-argparse-cli, sphinx-automodapi, sphinx-copybutton, recommonmark + +## Tests +- [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) + +## 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. + +## 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`. +- [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 - [ ] **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. - - 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` -- [ ] 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. -- [ ] 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! -- [ ] 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 -- [ ] 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 -- [ ] 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 + - 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()``). +``` -## Py3-related stuff for 1.0.0 -- [ ] 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 -- [ ] rewrite strings to f-strings -- [ ] 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 -- [ ] update classifiers in setup.py -- [ ] add inline type annotations for method arguments. remove types from docstrings? -- [ ] Remove `shutilwhich.py` and `.coveragerc` + +## Enhancements/fixes/misc. +- [x] Python 3.13 + 3.14 +- [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. + +## Before releasing +- [x] Update supported versions table in [SECURITY.md](../SECURITY.md) + +## 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 +- [x] BUMP TEST DEPENDENCIES AND PYTEST VERSION TO MODERN TIMES (especially pytest...) +- [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] 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 -- [ ] Refactor the default interface code. Combine the functions into +- [ ] 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?) + # 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?) @@ -107,6 +103,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 @@ -142,37 +147,66 @@ 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. -- [ ] 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.) + + +# 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 +- [ ] 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+) +- [ ] 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 -- [ ] 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)) +- [ ] 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 +- [ ] 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) +- [ ] 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?) -- [ ] 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" -- [ ] 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 -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/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/misc_docs/releasing.md b/docs/misc_docs/releasing.md new file mode 100644 index 0000000..2eb5f2a --- /dev/null +++ b/docs/misc_docs/releasing.md @@ -0,0 +1,26 @@ +## Requirements +- 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` (in `__version__`) +1. Update CHANGELOG header from UNRELEASED to the version and add the date +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 +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 +pdm build +``` +1. Upload the sdist (`.tar.gz`) and wheel (`.whl`) to PyPI +```bash +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`) 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 diff --git a/docs/releasing.md b/docs/releasing.md deleted file mode 100644 index 6903eab..0000000 --- a/docs/releasing.md +++ /dev/null @@ -1,42 +0,0 @@ - -## 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 -``` - -## 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 -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`) -```bash -python setup.py sdist bdist_wheel --universal -``` -8. 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: - 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 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/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 new file mode 100644 index 0000000..aa3e262 --- /dev/null +++ b/docs/usage.rst @@ -0,0 +1,159 @@ +===== +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 + + 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 + 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) + 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_iface = IPv4Interface("192.168.0.1/24") + print(get_mac_address(ip=ipv4_iface)) + + 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 :func:`~getmac.getmac.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 +============= + +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 ``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:`~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 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 `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. +- 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. diff --git a/getmac/__init__.py b/getmac/__init__.py index d0e9a04..ed47bfc 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, settings # noqa: F401 __all__ = ["get_mac_address"] diff --git a/getmac/__main__.py b/getmac/__main__.py index 2b51733..332fb58 100644 --- a/getmac/__main__.py +++ b/getmac/__main__.py @@ -1,34 +1,50 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from __future__ import print_function +#!/usr/bin/env python3 import argparse import logging import sys from . import getmac +from .variables import gvars, settings -def main(): # type: () -> None +def build_parser() -> argparse.ArgumentParser: 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="getmac %s" % getmac.__version__ + parser.add_argument("--version", action="version", version=f"getmac {getmac.__version__}") + + 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( - "-v", "--verbose", action="store_true", help="Enable output messages" + group.add_argument( + "-4", + "--ip", + type=str, + default=None, + help="IPv4 address of a remote host", ) - parser.add_argument( - "-d", - "--debug", - action="count", - help="Enable debugging output. Add characters to " - "increase verbosity of output, e.g. '-dd'.", + 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", @@ -37,14 +53,29 @@ def main(): # type: () -> 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, @@ -53,7 +84,7 @@ def main(): # type: () -> 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, @@ -63,46 +94,33 @@ def main(): # type: () -> 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" - ) + return parser + +def main() -> None: + parser = build_parser() args = parser.parse_args() 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: - 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 - ) - getmac.PORT = port + gvars.log.debug(f"Using UDP port {port} (overriding the default port {settings.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, @@ -113,7 +131,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 7587ec1..26c88cb 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. @@ -9,308 +6,54 @@ - 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 - 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) """ + import ctypes -import logging import os -import platform import re -import shlex import socket import struct -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 - -# 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 -except ImportError: - pass - -# Configure logging -log = logging.getLogger("getmac") # type: logging.Logger -if not log.handlers: - log.addHandler(logging.NullHandler()) - -__version__ = "0.9.5" - -PY2 = sys.version_info[0] == 2 # type: bool - -# 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) - -WINDOWS = _SYST == "Windows" # type: bool -DARWIN = _SYST == "Darwin" # type: bool - -OPENBSD = _SYST == "OpenBSD" # type: bool -FREEBSD = _SYST == "FreeBSD" # type: bool -NETBSD = _SYST == "NetBSD" # type: bool -SOLARIS = _SYST == "SunOS" # type: bool - -# Not including Darwin or Solaris as a "BSD" -BSD = OPENBSD or FREEBSD or NETBSD # type: bool - -# Windows Subsystem for Linux (WSL) -WSL = False # type: bool -LINUX = False # type: bool -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 = ( - hasattr(sys, "getandroidapilevel") or "ANDROID_STORAGE" in os.environ -) # type: bool - -# Generic platform identifier used for filtering methods -PLATFORM = _SYST.lower() # type: str -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 - -# Force a specific method to be used for all lookups -# Used for debugging and testing -FORCE_METHOD = "" # type: 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] -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 = os.pathsep.join(PATH) # type: str - -# 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["LC_ALL"] = "C" # Ensure ASCII output so we parse correctly - -# Constants -IP4 = 0 -IP6 = 1 -INTERFACE = 2 -HOSTNAME = 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})" -# 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})" - -# 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] - - -def check_command(command): - # type: (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 - return os.path.exists(filepath) and os.access(filepath, os.R_OK) - - -def _clean_mac(mac): - # type: (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 (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, "") - - # 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: - 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( - "Length of MAC %s is %d, padding single-character octets with zeros", - mac, - len(mac), - ) - 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: - 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) - mac = None - return mac - - -def _read_file(filepath): - # type: (str) -> Optional[str] - try: - with open(filepath) as f: - return f.read() - # This is IOError on Python 2.7 - except (OSError, IOError): # noqa: B014 - log.debug("Could not find file: '%s'", filepath) - return None - - -def _search(regex, text, group_index=0, flags=0): - # type: (str, str, int, int) -> Optional[str] - if not text: - if DEBUG: - 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, args): - # type: (str, str) -> str - for directory in 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 DEBUG >= 3: - log.debug("Running: '%s %s'", executable, args) - - return _call_proc(executable, args) - - -def _call_proc(executable, args): - # type: (str, str) -> str - if WINDOWS: - cmd = executable + " " + args # type: ignore - else: - cmd = [executable] + shlex.split(args) # type: ignore - - output = check_output(cmd, stderr=DEVNULL, env=ENV) - - 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) +from ipaddress import ( + IPv4Address, + IPv4Interface, + IPv4Network, + IPv6Address, + IPv6Interface, + IPv6Network, +) +from subprocess import CalledProcessError +from typing import Dict, Final, List, Optional, Set, Tuple, Type, Union +from . import utils +from .variables import consts, gvars, settings -def _uuid_convert(mac): - # type: (int) -> str - return ":".join(("%012X" % mac)[i : i + 2] for i in range(0, 12, 2)) +#: Current version of getmac package +__version__ = "1.0.0a0" -def _fetch_ip_using_dns(): - # type: () -> str +class Method: """ - 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. + Base class defining a method to get a MAC address. + Subclasses should implement the :meth:`test` and :meth:`get` methods. """ - 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 - -class Method: - #: Valid platform identifier strings - VALID_PLATFORM_NAMES = { + VALID_PLATFORM_NAMES: Final[Set[str]] = { "android", "darwin", "linux", @@ -319,29 +62,51 @@ class Method: "openbsd", "freebsd", "sunos", + "hp-ux", "other", } + """ + The valid platform identifier strings, used to match + methods to the appropriate platform. + """ - #: Platforms supported by a method - platforms = set() # type: Set[str] + 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 = "" # type: str + method_type: str = "" + """ + The type of method, e.g. does it get the MAC of a interface. - #: If the method makes a network request as part of the check - network_request = False # type: bool + Allowed values: - #: 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 + - ip + - ip4 + - ip6 + - iface + - default_iface + """ - def test(self): # type: () -> bool # noqa: T484 - """Low-impact test that the method is feasible, e.g. command exists.""" - pass # pragma: no cover + network_request: bool = False + """ + If the method makes a network request as part of the check. + """ + + 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. a 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. @@ -351,127 +116,124 @@ def get(self, arg): # type: (str) -> Optional[str] internal error with the command, or a bug in the code). Args: - arg (str): What the method should get, such as an IP address - or interface name. In the case of default_iface methods, + 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. 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. """ - pass # pragma: no cover + return None # pragma: no cover @classmethod - def __str__(cls): # type: () -> str + 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): - platforms = {"linux", "darwin", "sunos", "other"} - method_type = "ip" - - def test(self): # type: () -> bool - try: - from uuid import _arp_getnode # type: ignore - - return True - except Exception: - return False - - def get(self, arg): # type: (str) -> Optional[str] - from uuid import _arp_getnode # type: ignore - - backup = socket.gethostbyname - try: - socket.gethostbyname = lambda x: arg # noqa: F841 - mac1 = _arp_getnode() - if mac1 is not None: - mac1 = _uuid_convert(mac1) - mac2 = _arp_getnode() - mac2 = _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. + """ + platforms = {"linux"} method_type = "ip4" - _path = os.environ.get("ARP_PATH", "/proc/net/arp") # type: str - def test(self): # type: () -> bool - return check_path(self._path) + _path: Final[str] = os.environ.get("ARP_PATH", "/proc/net/arp") + + def test(self) -> bool: + return utils.check_path(self._path) - def get(self, arg): # type: (str) -> Optional[str] + 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 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) + return utils.search(re.escape(arg) + r" .+" + consts.MAC_RE_COLON, data) return None class ArpFreebsd(Method): + """ + Use the ``arp`` command to find the MAC address of a host on FreeBSD. + """ + platforms = {"freebsd"} method_type = "ip" - def test(self): # type: () -> bool - return check_command("arp") + def test(self) -> bool: + return utils.check_command("arp") - def get(self, arg): # type: (str) -> Optional[str] - regex = r"\(" + re.escape(arg) + r"\)\s+at\s+" + MAC_RE_COLON - return _search(regex, _popen("arp", arg)) + def get(self, arg: str) -> Optional[str]: + regex = r"\(" + re.escape(arg) + r"\)\s+at\s+" + consts.MAC_RE_COLON + return utils.search(regex, utils.popen("arp", arg)) class ArpOpenbsd(Method): + """ + Use the ``arp`` command to find the MAC address of a host on OpenBSD. + """ + platforms = {"openbsd"} method_type = "ip" - _regex = r"[ ]+" + MAC_RE_COLON # type: str - def test(self): # type: () -> bool - return check_command("arp") + _regex: Final[str] = r"[ ]+" + consts.MAC_RE_COLON - def get(self, arg): # type: (str) -> Optional[str] - return _search(re.escape(arg) + self._regex, _popen("arp", "-an")) + def test(self) -> bool: + return utils.check_command("arp") + + def get(self, arg: str) -> Optional[str]: + return utils.search(re.escape(arg) + self._regex, utils.popen("arp", "-an")) 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 = r"\)\s+at\s+" + MAC_RE_COLON # type: str - _regex_darwin = r"\)\s+at\s+" + MAC_RE_SHORT # type: str + + _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 + # 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??? ("-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]] - def test(self): # type: () -> bool - return check_command("arp") + # If arguments have been tested + _args_tested: bool = False + # arguments that worked + _good_pair: Union[Tuple, Tuple[str, bool]] = () + + def test(self) -> bool: + return utils.check_command("arp") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: if not arg: return None @@ -489,23 +251,25 @@ def get(self, arg): # type: (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: - if DEBUG: - log.debug( - "ArpVariousArgs pair test failed for (%s, %s): %s", - pair_to_test[0], - pair_to_test[1], - str(ex), + if settings.DEBUG: + gvars.log.debug( + f"ArpVariousArgs pair test failed for " + 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]] @@ -513,13 +277,15 @@ def get(self, arg): # type: (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)) + + # 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 - escaped = re.escape(arg) - _good_regex = ( - self._regex_darwin if DARWIN or SOLARIS else self._regex_std - ) # type: str - return _search(r"\(" + escaped + _good_regex, command_output) + return utils.search(regex, command_output) class ArpExe(Method): @@ -528,59 +294,62 @@ 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" - 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") + return utils.check_command("arp.exe") - def get(self, arg): # type: (str) -> Optional[str] - return _search(MAC_RE_DASH, _popen("arp.exe", "-a %s" % arg)) + def get(self, arg: str) -> Optional[str]: + return utils.search(consts.MAC_RE_DASH, utils.popen("arp.exe", f"-a {arg}")) 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"} 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 - def test(self): # type: () -> bool - return check_command("arping") + _is_iputils: bool = True + _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") - 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. # @@ -590,24 +359,22 @@ def get(self, arg): # type: (str) -> Optional[str] # then re-try with Habets args. try: if self._is_iputils: - command_output = _popen("arping", self._iputils_args % arg) + command_output = utils.popen("arping", f"{self._iputils_args} {arg}") if command_output: - return _search( - r" from %s \[(%s)\]" % (re.escape(arg), MAC_RE_COLON), + return utils.search( + r" from %s \[(%s)\]" % (re.escape(arg), consts.MAC_RE_COLON), command_output, ) else: 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: - 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) @@ -616,8 +383,8 @@ def get(self, arg): # type: (str) -> Optional[str] return None - def _call_habets(self, arg): # type: (str) -> Optional[str] - command_output = _popen("arping", self._habets_args % arg) + def _call_habets(self, arg: str) -> Optional[str]: + command_output = utils.popen("arping", f"{self._habets_args} {arg}") if command_output: return command_output.strip() else: @@ -629,25 +396,26 @@ 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) `__ - """ # noqa: E501 + """ platforms = {"windows"} 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 # noqa: T484 + return ctypes.windll.wsock32.inet_addr(b"127.0.0.1") > 0 # type: ignore except Exception: 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 - + def get(self, arg: str) -> Optional[str]: try: - inetaddr = ctypes.windll.wsock32.inet_addr(arg) # type: ignore + # 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: @@ -678,38 +446,47 @@ def get(self, arg): # type: (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 - def test(self): # type: () -> bool - return check_command("ip") + def test(self) -> bool: + return utils.check_command("ip") - def get(self, arg): # type: (str) -> Optional[str] - output = _popen("ip", "neighbor show %s" % arg) + def get(self, arg: str) -> Optional[str]: + output = utils.popen("ip", f"neighbor show {arg}") if not output: return None 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: - log.debug("IpNeighborShow failed with exception: %s", str(ex)) + gvars.log.debug(f"IpNeighborShow failed with exception: {ex}") return None 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" - _path = "/sys/class/net/" # type: str - def test(self): # type: () -> bool + _path: Final[str] = "/sys/class/net/" + + 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): # type: (str) -> Optional[str] - data = _read_file(self._path + arg + "/address") + def get(self, arg: str) -> Optional[str]: + 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 @@ -717,59 +494,69 @@ def get(self, arg): # type: (str) -> Optional[str] return None if data is not None and len(data) < 17 else data -class UuidLanscan(Method): - platforms = {"other"} - method_type = "iface" - - def test(self): # type: () -> bool - try: - from uuid import _find_mac # noqa: T484 +class LanscanIface(Method): + """ + Uses the ``lanscan`` command to get the MAC address of a network interface on HP-UX. - return check_command("lanscan") - except Exception: - return False + This is adopted from Python's :mod:`uuid` module's ``_lanscan_getnode`` function. + """ - def get(self, arg): # type: (str) -> Optional[str] - from uuid import _find_mac # type: ignore + platforms = {"hp-ux"} + method_type = "iface" - if not PY2: - arg = bytes(arg, "utf-8") # type: ignore + def test(self) -> bool: + return utils.check_command("lanscan") - mac = _find_mac("lanscan", "-ai", [arg], lambda i: 0) + 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 - if mac: - return _uuid_convert(mac) + # 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 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" - def test(self): # type: () -> bool + def test(self) -> bool: try: - import fcntl + import fcntl # noqa: F401 return True 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 - if not PY2: - 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])) + info = fcntl.ioctl( # type: ignore + s.fileno(), 0x8927, struct.pack("256s", encoded_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): @@ -777,40 +564,41 @@ 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" - _regexes = [ + + _regexes: Final[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"), - ] # type: List[Tuple[str, str]] - _champ = () # type: Union[tuple, Tuple[str, str]] + (r"\r\n.*", r".*" + consts.MAC_RE_DASH + r".*\r\n"), + ] + _champ: 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") + return utils.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 - 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 - 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 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 @@ -826,19 +614,18 @@ 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" - _regex = ( - r"(?:\n?[^\n]*){1,8}Physical Address[ .:]+" + MAC_RE_DASH + r"\r\n" - ) # type: str - def test(self): # type: () -> bool - return check_command("ipconfig.exe") + _regex: Final[str] = r"(?:\n?[^\n]*){1,8}Physical Address[ .:]+" + consts.MAC_RE_DASH + r"\r\n" - def get(self, arg): # type: (str) -> Optional[str] - return _search(arg + self._regex, _popen("ipconfig.exe", "/all")) + def test(self) -> bool: + return utils.check_command("ipconfig.exe") + + def get(self, arg: str) -> Optional[str]: + return utils.search(arg + self._regex, utils.popen("ipconfig.exe", "/all")) class WmicExe(Method): @@ -851,18 +638,18 @@ 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" - def test(self): # type: () -> bool - return check_command("wmic.exe") + def test(self) -> bool: + return utils.check_command("wmic.exe") - def get(self, arg): # type: (str) -> Optional[str] - command_output = _popen( + def get(self, arg: str) -> Optional[str]: + command_output = utils.popen( "wmic.exe", - 'nic where "NetConnectionID = \'%s\'" get "MACAddress" /value' % arg, + f'nic where "NetConnectionID = \'{arg}\'" get "MACAddress" /value', ) # Negative: "No Instance(s) Available" @@ -889,12 +676,12 @@ class DarwinNetworksetupIface(Method): platforms = {"darwin"} method_type = "iface" - def test(self): # type: () -> bool - return check_command("networksetup") + def test(self) -> bool: + return utils.check_command("networksetup") - def get(self, arg): # type: (str) -> Optional[str] - command_output = _popen("networksetup", "-getmacaddress %s" % arg) - return _search(MAC_RE_COLON, command_output) + def get(self, arg: str) -> Optional[str]: + 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 @@ -902,7 +689,7 @@ def get(self, arg): # type: (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 @@ -910,8 +697,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 @@ -921,24 +707,24 @@ def _parse_ifconfig(iface, command_output): # "(?:^|\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): """ ``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"} method_type = "iface" - def test(self): # type: () -> bool - return check_command("ifconfig") + def test(self) -> bool: + return utils.check_command("ifconfig") - def get(self, arg): # type: (str) -> Optional[str] + 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: @@ -952,30 +738,36 @@ def get(self, arg): # type: (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" - _tested_arg = False # type: bool - _iface_arg = False # type: bool - def test(self): # type: () -> bool - return check_command("ifconfig") + _tested_arg: bool = False + _iface_arg: bool = False + + def test(self) -> bool: + return utils.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 = "" 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) @@ -984,7 +776,8 @@ def get(self, arg): # type: (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"} @@ -996,13 +789,13 @@ 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): # type: () -> bool - return check_command("ifconfig") + def test(self) -> bool: + return utils.check_command("ifconfig") - def get(self, arg): # type: (str) -> Optional[str] + def get(self, arg: str) -> Optional[str]: if not arg: return None @@ -1013,18 +806,16 @@ def get(self, arg): # type: (str) -> Optional[str] if not self._args_tested: 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 + 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] += MAC_RE_COLON + self._good_pair[1] += consts.MAC_RE_COLON break except CalledProcessError as ex: - if DEBUG: - log.debug( - "IfconfigOther pair test failed for (%s, %s): %s", - pair_to_test[0], - pair_to_test[1], - str(ex), + if settings.DEBUG: + gvars.log.debug( + f"IfconfigOther pair test failed for " + f"({pair_to_test[0]}, {pair_to_test[1]}): {ex}" ) if not self._good_pair: @@ -1034,13 +825,13 @@ def get(self, arg): # type: (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 + MAC_RE_COLON - result = _search(re.escape(arg) + regex, command_output) + regex = term + consts.MAC_RE_COLON + result = utils.search(re.escape(arg) + regex, command_output) if result: # changes type from tuple to str, so the else statement @@ -1049,44 +840,48 @@ def get(self, arg): # type: (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): + """ + Determines interface MAC using the ``netstat`` command. + """ + platforms = {"linux", "wsl", "other"} method_type = "iface" # ".*?": non-greedy # https://docs.python.org/3/howto/regex.html#greedy-versus-non-greedy - _regexes = [ - r": .*?ether " + MAC_RE_COLON, - r": .*?HWaddr " + MAC_RE_COLON, + _regexes: Final[List[str]] = [ + 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, - ] # type: List[str] - _working_regex = "" # type: str + r" .*?Link encap:Ethernet HWaddr " + consts.MAC_RE_COLON, + ] + _working_regex: str = "" - def test(self): # type: () -> bool - return check_command("netstat") + def test(self) -> bool: + return utils.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") + command_output = utils.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 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 @@ -1096,29 +891,34 @@ def get(self, arg): # type: (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): + """ + Determines interface MAC using the ``ip link`` command. + """ + 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 - def test(self): # type: () -> bool - return check_command("ip") + _regex: Final[str] = r".*\n.*link/ether " + consts.MAC_RE_COLON + _tested_arg: bool = False + _iface_arg: bool = False + + def test(self) -> bool: + return utils.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? @@ -1126,7 +926,7 @@ def get(self, arg): # type: (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"' @@ -1136,17 +936,18 @@ def get(self, arg): # type: (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): """ - 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 @@ -1157,11 +958,13 @@ class DefaultIfaceLinuxRouteFile(Method): platforms = {"linux", "wsl"} method_type = "default_iface" - def test(self): # type: () -> bool - return check_path("/proc/net/route") + _path: Final[str] = "/proc/net/route" - def get(self, arg=""): # type: (str) -> Optional[str] - data = _read_file("/proc/net/route") + def test(self) -> bool: + return utils.check_path(self._path) + + def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 + data = utils.read_file(self._path) if data is not None and len(data) > 1: for line in data.split("\n")[1:-1]: @@ -1180,43 +983,57 @@ def get(self, arg=""): # type: (str) -> Optional[str] 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" + f"'{self._path}', no destination of '00000000' was found" ) - elif DEBUG: - log.warning("No data from /proc/net/route") + elif settings.DEBUG: + gvars.log.warning(f"No data from {self._path}") return None class DefaultIfaceRouteCommand(Method): + """ + Determine default interface using the ``route -n`` command. + """ + platforms = {"linux", "wsl", "other"} method_type = "default_iface" - def test(self): # type: () -> bool - return check_command("route") + def test(self) -> bool: + return utils.check_command("route") - def get(self, arg=""): # type: (str) -> Optional[str] - output = _popen("route", "-n") + def get(self, arg: str = "") -> Optional[str]: + output = utils.popen("route", "-n") try: - return output.partition("0.0.0.0")[2].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 ( + 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? + gvars.log.debug(f"DefaultIfaceRouteCommand failed for {arg}: {ex}") return None 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" - def test(self): # type: () -> bool - return check_command("route") + def test(self) -> bool: + return utils.check_command("route") - def get(self, arg=""): # type: (str) -> Optional[str] - output = _popen("route", "get default") + def get(self, arg: str = "") -> Optional[str]: + output = utils.popen("route", "get default") if not output: return None @@ -1224,52 +1041,68 @@ def get(self, arg=""): # type: (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(f"DefaultIfaceRouteCommand failed for {arg}: {ex}") return None 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"} method_type = "default_iface" - def test(self): # type: () -> bool - return check_command("ip") + def test(self) -> bool: + return utils.check_command("ip") - def get(self, arg=""): # type: (str) -> Optional[str] - output = _popen("ip", "route list 0/0") + def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 + output = utils.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() 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" - def test(self): # type: () -> bool - return check_command("route") + def test(self) -> bool: + return utils.check_command("route") - def get(self, arg=""): # type: (str) -> Optional[str] - output = _popen("route", "-nq show -inet -gateway -priority 1") + def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 + output = utils.popen("route", "-nq show -inet -gateway -priority 1") return output.partition("127.0.0.1")[0].strip().rpartition(" ")[2] 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" - def test(self): # type: () -> bool - return check_command("netstat") + def test(self) -> bool: + return utils.check_command("netstat") - def get(self, arg=""): # type: (str) -> Optional[str] - output = _popen("netstat", "-r") - return _search(r"default[ ]+\S+[ ]+\S+[ ]+(\S+)[\r\n]+", output) + def get(self, arg: str = "") -> Optional[str]: # noqa: ARG002 + output = utils.popen("netstat", "-r") + return utils.search(r"default[ ]+\S+[ ]+\S+[ ]+(\S+)[\r\n]+", output) # TODO: order methods by effectiveness/reliability @@ -1281,7 +1114,7 @@ def get(self, arg=""): # type: (str) -> Optional[str] ArpingHost, SysIfaceFile, FcntlIface, - UuidLanscan, + LanscanIface, GetmacExe, IpconfigExe, WmicExe, @@ -1296,7 +1129,6 @@ def get(self, arg=""): # type: (str) -> Optional[str] NetstatIface, IpNeighborShow, ArpVariousArgs, - UuidArpGetNode, DefaultIfaceLinuxRouteFile, DefaultIfaceIpRoute, DefaultIfaceRouteCommand, @@ -1305,32 +1137,36 @@ def get(self, arg=""): # type: (str) -> Optional[str] DefaultIfaceFreeBsd, ] # type: List[Type[Method]] -# Primary method to use for a given method type -METHOD_CACHE = { +# 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, -} # type: Dict[str, Optional[Method]] +} +""" +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) -FALLBACK_CACHE = { +# TODO: move to gvars class? +FALLBACK_CACHE: Dict[str, List[Method]] = { "ip4": [], "ip6": [], "iface": [], "default_iface": [], -} # type: Dict[str, List[Method]] - +} +""" +Order of methods is determined by: -DEFAULT_IFACE = "" # type: str +- Platform + version +- Performance (file read > command) +- Reliability (how well I know/understand the command to work) +""" -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 @@ -1338,18 +1174,21 @@ 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. + 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: @@ -1362,8 +1201,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 @@ -1379,24 +1217,12 @@ 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 -def _warn_critical(err_msg): - # type: (str) -> None - log.critical(err_msg) - warnings.warn( - "%s. NOTICE: this warning will likely turn into a raised exception in getmac 1.0.0!" - % err_msg, - RuntimeWarning, - ) - - -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. @@ -1404,31 +1230,35 @@ def initialize_method_cache( 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 + + 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 DEBUG: - log.debug( - "Method cache already initialized for method type '%s'", method_type - ) + if settings.DEBUG: + gvars.log.debug(f"Method cache already initialized for method type '{method_type}'") return True - log.debug("Initializing '%s' method cache (platform: '%s')", method_type, PLATFORM) + gvars.log.debug(f"Initializing '{method_type}' method cache (platform: '{consts.PLATFORM}')") - if OVERRIDE_PLATFORM: - log.warning( - "Platform override is set, using '%s' as platform " - "instead of detected platform '%s'", - OVERRIDE_PLATFORM, - PLATFORM, + if settings.OVERRIDE_PLATFORM: + gvars.log.warning( + f"Platform override is set, using '{settings.OVERRIDE_PLATFORM}' as platform " + f"instead of detected platform '{consts.PLATFORM}'" ) - platform = OVERRIDE_PLATFORM + platform = settings.OVERRIDE_PLATFORM else: - platform = PLATFORM + platform = consts.PLATFORM - if DEBUG >= 4: - meth_strs = ", ".join(m.__name__ for m in METHODS) # type: str - log.debug("%d methods available: %s", len(METHODS), meth_strs) + if settings.DEBUG >= 4: + meth_strs = ", ".join(m.__name__ for m in METHODS) + 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. @@ -1441,52 +1271,40 @@ def initialize_method_cache( ] # type: List[Type[Method]] if not type_methods: - _warn_critical("No valid methods matching MAC type '%s'" % method_type) - return False + raise RuntimeError(f"No valid methods matching MAC type '{method_type}'") - if DEBUG >= 2: - type_strs = ", ".join(tm.__name__ for tm in type_methods) # type: str - log.debug( - "%d type-filtered methods for '%s': %s", - len(type_methods), - method_type, - type_strs, + if settings.DEBUG >= 2: + type_strs = ", ".join(tm.__name__ for tm in type_methods) + gvars.log.debug( + f"{len(type_methods)} type-filtered methods for '{method_type}': {type_strs}" ) # 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, # 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'." ) - log.warning(warn_msg) - warnings.warn(warn_msg, RuntimeWarning) - platform_methods = [ - method for method in type_methods if "other" in method.platforms - ] - - if DEBUG >= 2: - plat_strs = ", ".join(pm.__name__ for pm in platform_methods) # type: str - log.debug( - "%d platform-filtered methods for '%s' (method_type='%s'): %s", - len(platform_methods), - platform, - method_type, - plat_strs, + 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 settings.DEBUG >= 2: + plat_strs = ", ".join(pm.__name__ for pm in platform_methods) + gvars.log.debug( + 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) + 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]] @@ -1500,7 +1318,7 @@ def initialize_method_cache( 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: @@ -1508,75 +1326,59 @@ def initialize_method_cache( # 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(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) - ) - return False + raise RuntimeError(f"All {len(filtered_methods)} '{method_type}' methods failed to test!") - if DEBUG >= 2: - tested_strs = ", ".join(str(ts) for ts in tested_methods) # type: str - log.debug( - "%d tested methods for '%s': %s", - len(tested_methods), - method_type, - tested_strs, - ) + 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}") # 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 - 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(f"Finished initializing '{method_type}' 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)) + 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) - log.warning( - "Falling back to '%s' for unusable method '%s'", - str(METHOD_CACHE[method_type]), - str(method), + gvars.log.warning( + f"Falling back to '{METHOD_CACHE[method_type]!s}' for unusable method '{method!s}'" ) 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. """ if not METHOD_CACHE[method_type] and not FALLBACK_CACHE[method_type]: - _warn_critical("No usable methods found for MAC type '%s'" % method_type) - return None + raise RuntimeError(f"No usable methods found for MAC type '{method_type}'") - if DEBUG: - log.debug( - "Attempting get() (method='%s', method_type='%s', arg='%s')", - str(method), - method_type, - arg, + if settings.DEBUG: + gvars.log.debug( + f"Attempting get() (method='{method!s}', method_type='{method_type}', arg='{arg}')" ) result = None @@ -1592,21 +1394,15 @@ def _attempt_method_get( # 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( - "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), + gvars.log.warning( + 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: - log.warning( - "Cached Method '%s' failed for '%s' lookup with unhandled exception: %s", - str(method), - method_type, - str(ex), + gvars.log.warning( + f"Cached Method '{method!s}' failed for '{method_type}' " + f"lookup with unhandled exception: {ex}" ) method.unusable = True @@ -1623,8 +1419,7 @@ 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. @@ -1633,24 +1428,25 @@ def get_by_method(method_type, arg="", network_request=True): 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": - 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 FORCE_METHOD: - log.warning( - "Forcing method '%s' to be used for '%s' lookup (arg: '%s')", - FORCE_METHOD, - method_type, - arg, + if settings.FORCE_METHOD: + gvars.log.warning( + f"Forcing method '{settings.FORCE_METHOD}' to be used for " + f"'{method_type}' lookup (arg: '{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(f"Invalid FORCE_METHOD method name '{settings.FORCE_METHOD}'") return None return forced_method().get(arg) @@ -1660,20 +1456,17 @@ def get_by_method(method_type, arg="", network_request=True): if not method: # Initialize the cache if it hasn't been already if not initialize_method_cache(method_type, network_request): - log.error( - "Failed to initialize method cache for method '%s' (arg: '%s')", - method_type, - arg, + gvars.log.error( + f"Failed to initialize method cache for method '{method_type}' (arg: '{arg}')" ) return None method = METHOD_CACHE[method_type] if not method: - log.error( - "Initialization failed for method '%s'. It may not be supported " - "on this platform or another issue occurred.", - method_type, + gvars.log.error( + f"Initialization failed for method '{method_type}'. " + f"It may not be supported on this platform or another issue occurred." ) return None @@ -1683,76 +1476,115 @@ def get_by_method(method_type, arg="", network_request=True): 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(f"Method '{method!s}' failed for '{method_type}' lookup") return result -def get_mac_address( # noqa: C901 - interface=None, ip=None, ip6=None, hostname=None, network_request=True -): - # type: (Optional[str], Optional[str], Optional[str], Optional[str], bool) -> Optional[str] +def get_mac_address( + 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 address from a local interface or remote host. - 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. - .. 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). - - .. warning:: - You MUST provide :class:`str` typed arguments, REGARDLESS of Python version + The MAC is usually a unicast IEEE 802 MAC-48 address. .. 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`. + 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` + 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 module variable ``getmac.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. - """ # noqa: E501 + Lowercase colon-separated MAC address. If no MAC was found, or an exception + occurred, :obj:`None` is returned. - if DEBUG: + 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 + if settings.DEBUG: import timeit start_time = timeit.default_timer() - if PY2 or (sys.version_info[0] == 3 and sys.version_info[1] < 7): - global WARNED_UNSUPPORTED_PYTHONS - if not WARNED_UNSUPPORTED_PYTHONS: - warning_string = ( - "Support for Python versions < 3.7 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.0.0" or "getmac~=0.9.0".' + # Convert bytes to str, assuming UTF-8 encoding + if isinstance(interface, bytes): + interface = interface.decode("utf-8") + if isinstance(ip, bytes): + ip = ip.decode("utf-8") + if isinstance(ip6, bytes): + ip6 = ip6.decode("utf-8") + if isinstance(hostname, bytes): + hostname = hostname.decode("utf-8") + + # Handle ipaddress objects + # "is not None" check makes mypy happier + if ip is not None and not isinstance(ip, str): + # 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, " + "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. + # 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, " + "not a network. Try IPv6Address or IPv6Interface instead." ) - warnings.warn(warning_string, DeprecationWarning) - log.warning(warning_string) # Ensure it appears in any logs - WARNED_UNSUPPORTED_PYTHONS = True + 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" @@ -1764,26 +1596,41 @@ def get_mac_address( # noqa: C901 # 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(f"Could not resolve hostname '{hostname}': {ex}") + if settings.DEBUG: + 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: - log.error( - "Cannot get the MAC address of a IPv6 host: " - "IPv6 is not supported on this system" + # 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 ":" not in ip6: - log.error("Invalid IPv6 address (no ':'): %s", ip6) + + # 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, " + "not a network. Try IPv6Address or IPv6Interface instead." + ) + 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 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 @@ -1795,7 +1642,7 @@ def get_mac_address( # noqa: C901 # 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) @@ -1807,13 +1654,14 @@ def get_mac_address( # noqa: C901 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"]): 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 @@ -1821,11 +1669,10 @@ def get_mac_address( # noqa: C901 # 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( - "Attempting to populate ARP table with UDP packet to %s:%d", - ip if ip else ip6, - PORT, + if settings.DEBUG: + gvars.log.debug( + f"Attempting to populate ARP table with UDP packet " + f"to {ip if ip else ip6}:{settings.PORT}" ) if ip: @@ -1835,19 +1682,19 @@ def get_mac_address( # noqa: C901 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( - "Not sending UDP packet, using network request method '%s' instead", - str(METHOD_CACHE["ip4"]), + elif settings.DEBUG: + gvars.log.debug( + f"Not sending UDP packet, using network request " + f"method '{METHOD_CACHE['ip4']!s}' instead" ) # Setup the address hunt based on the arguments specified @@ -1858,43 +1705,63 @@ def get_mac_address( # noqa: C901 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 WINDOWS and network_request: - default_iface_ip = _fetch_ip_using_dns() - mac = get_by_method("ip4", default_iface_ip) - elif 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") # noqa: T484 + 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 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" + # 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 and consts.HPUX: + gvars.DEFAULT_IFACE = "lan0" + 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") + # 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(f"Raw MAC found: {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(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. - return _clean_mac(mac) + 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/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/getmac/utils.py b/getmac/utils.py new file mode 100644 index 0000000..c652244 --- /dev/null +++ b/getmac/utils.py @@ -0,0 +1,243 @@ +""" +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 shutil +import socket +import subprocess +from typing import Optional, Union + +from .variables import consts, gvars, settings + + +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 :class:`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(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. + + 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) + + +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 + + # 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]: + """ + 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() + 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]: + """ + 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: 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 + or nothing was specified to search. + """ + 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: + """ + 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) + 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: + """ + Wrapper around :func:`subprocess.check_output` with some + 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 + 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: + cmd = [executable, *shlex.split(args)] # type: ignore + + 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}") + + if isinstance(output, bytes): + output = output.decode("utf-8") + + return output + + +def fetch_ip_using_dns() -> str: + """ + 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, + 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)) + return s.getsockname()[0] diff --git a/getmac/variables.py b/getmac/variables.py new file mode 100644 index 0000000..0648ab7 --- /dev/null +++ b/getmac/variables.py @@ -0,0 +1,237 @@ +""" +Global variables, constants, and settings for the getmac package. +""" + +import logging +import os +import platform +import sys +from typing import Dict, Final, List + + +class VarsClass: + pass + + +class Settings(VarsClass): + """ + User-configurable settings. + """ + + DEBUG: int = 0 + """ + Debugging level. Increased value => more output. 4 is roughly the highest. + """ + + 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. + """ + + # TODO: This will change to a function argument in 1.0.0 + OVERRIDE_PLATFORM: str = "" + """ + User-configurable override to force a specific platform. + """ + + FORCE_METHOD: str = "" + """ + Force a specific method to be used for all lookups. + Used for debugging and testing. + """ + + +class Constants(VarsClass): + """ + Platform identifiers and other constants. + """ + + _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" + HPUX: Final[bool] = _SYST == "HP-UX" + + 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 + ) + """ + 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 + ) + """ + 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). + + :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 + + Possible values: + + - wsl + - linux + - windows + - darwin + - openbsd + - freebsd + - netbsd + - sunos + - hp-ux + - 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. + """ + + 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: + """ + + 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: + """ + + 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. + """ + + log: logging.Logger = logging.getLogger("getmac") + """ + Global logger for getmac. The logger name is `getmac`. + + :meta hide-value: + """ + + DEFAULT_IFACE: str = "" + """ + Name of the local host's default network interface. + """ + + 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: + self.PATH.extend(("/sbin", "/usr/sbin")) + else: + # 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 aforementioned 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) + + +settings: Final[Settings] = Settings() +consts: Final[Constants] = Constants() +gvars: Final[Variables] = Variables() 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/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/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/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/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/pdm.lock b/pdm.lock new file mode 100644 index 0000000..82326aa --- /dev/null +++ b/pdm.lock @@ -0,0 +1,1328 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default", "docs", "lint", "test"] +strategy = ["inherit_metadata"] +lock_version = "4.5.0" +content_hash = "sha256:24daaff9ccccad500297e42c4a731c5629327b1a07b82fd90b364028705e8dac" + +[[metadata.targets]] +requires_python = "~=3.8" + +[[package]] +name = "alabaster" +version = "1.0.0" +requires_python = ">=3.10" +summary = "A light, configurable Sphinx theme" +groups = ["docs"] +marker = "python_version >= \"3.10\"" +files = [ + {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.17.0" +requires_python = ">=3.8" +summary = "Internationalization utilities" +groups = ["docs"] +marker = "python_version >= \"3.10\"" +dependencies = [ + "pytz>=2015.7; python_version < \"3.9\"", +] +files = [ + {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.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.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}, + {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, +] + +[[package]] +name = "certifi" +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-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.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.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 = "codespell" +version = "2.4.1" +requires_python = ">=3.8" +summary = "Fix common misspellings in text files" +groups = ["lint"] +files = [ + {file = "codespell-2.4.1-py3-none-any.whl", hash = "sha256:3dadafa67df7e4a3dbf51e0d7315061b80d265f9552ebd699b3dd6834b47e425"}, + {file = "codespell-2.4.1.tar.gz", hash = "sha256:299fcdcb09d23e81e35a671bbe746d5ad7e8385972e65dbb833a2eaac33c01e5"}, +] + +[[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 = ["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"}, +] + +[[package]] +name = "commonmark" +version = "0.9.1" +summary = "Python parser for the CommonMark Markdown spec" +groups = ["docs"] +marker = "python_version >= \"3.10\"" +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 = "7.6.1" +requires_python = ">=3.8" +summary = "Code coverage measurement for Python" +groups = ["test"] +files = [ + {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 = "7.6.1" +extras = ["toml"] +requires_python = ">=3.8" +summary = "Code coverage measurement for Python" +groups = ["test"] +dependencies = [ + "coverage==7.6.1", + "tomli; python_full_version <= \"3.11.0a6\"", +] +files = [ + {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 = "docutils" +version = "0.21.2" +requires_python = ">=3.9" +summary = "Docutils -- Python Documentation Utilities" +groups = ["docs"] +marker = "python_version >= \"3.10\"" +files = [ + {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, + {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, +] + +[[package]] +name = "exceptiongroup" +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.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, + {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, +] + +[[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 = "furo" +version = "2024.8.6" +requires_python = ">=3.8" +summary = "A clean customisable Sphinx documentation theme." +groups = ["docs"] +marker = "python_version >= \"3.10\"" +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.10" +requires_python = ">=3.6" +summary = "Internationalized Domain Names in Applications (IDNA)" +groups = ["docs"] +marker = "python_version >= \"3.10\"" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[[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.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"}, +] + +[[package]] +name = "importlib-metadata" +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>=3.20", +] +files = [ + {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.1.0" +requires_python = ">=3.8" +summary = "brain-dead simple config-ini parsing" +groups = ["test"] +files = [ + {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.6" +requires_python = ">=3.7" +summary = "A very fast and expressive template engine." +groups = ["docs"] +marker = "python_version >= \"3.10\"" +dependencies = [ + "MarkupSafe>=2.0", +] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {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" +requires_python = ">=3.9" +summary = "Safely add untrusted strings to HTML/XML markup." +groups = ["docs"] +marker = "python_version >= \"3.10\"" +files = [ + {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 = "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" +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.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.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.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {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" +requires_python = ">=3.8" +summary = "Core utilities for Python packages" +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 = "pluggy" +version = "1.5.0" +requires_python = ">=3.8" +summary = "plugin and hook calling mechanisms for python" +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"}, +] + +[[package]] +name = "psutil" +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-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]] +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.19.2" +requires_python = ">=3.8" +summary = "Pygments is a syntax highlighting package written in Python." +groups = ["docs", "test"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[[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.1" +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.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0"}, + {file = "pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e"}, +] + +[[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-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" +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 = "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" +summary = "A docutils-compatibility bridge to CommonMark, enabling you to write CommonMark inside of Docutils & Sphinx projects." +groups = ["docs"] +marker = "python_version >= \"3.10\"" +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.4" +requires_python = ">=3.8" +summary = "Python HTTP for Humans." +groups = ["docs"] +marker = "python_version >= \"3.10\"" +dependencies = [ + "certifi>=2017.4.17", + "charset-normalizer<4,>=2", + "idna<4,>=2.5", + "urllib3<3,>=1.21.1", +] +files = [ + {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.12.3" +requires_python = ">=3.7" +summary = "An extremely fast Python linter and code formatter, written in Rust." +groups = ["lint"] +files = [ + {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 = "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-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.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.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, + {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, +] + +[[package]] +name = "sphinx" +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", + "babel>=2.13", + "colorama>=0.4.6; sys_platform == \"win32\"", + "docutils<0.22,>=0.20", + "imagesize>=1.3", + "packaging>=23.0", + "requests>=2.30.0", + "snowballstemmer>=2.2", + "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-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.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>=8.0.2", +] +files = [ + {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 = "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>=8.1.3", +] +files = [ + {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.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.20.0-py3-none-any.whl", hash = "sha256:1d205d4f07c22b1024b270a0340b245b5f819bc4c625ca10db8c06e437cf5d5e"}, + {file = "sphinx_automodapi-0.20.0.tar.gz", hash = "sha256:24cc97323f163aeefcefb9bdae9cd0a8ea7b699ece34feab116a2557a44949ac"}, +] + +[[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.10\"" +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.10\"" +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 = "2023.4.21" +requires_python = ">=3.8" +summary = "Add inline tabbed content to your Sphinx documentation." +groups = ["docs"] +marker = "python_version >= \"3.10\"" +dependencies = [ + "sphinx>=3", +] +files = [ + {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]] +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.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"}, +] + +[[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.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"}, +] + +[[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.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"}, +] + +[[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.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"}, +] + +[[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.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"}, +] + +[[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.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"}, +] + +[[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" +requires_python = ">=3.8" +summary = "A lil' TOML parser" +groups = ["docs", "lint", "test"] +marker = "python_version < \"3.12\"" +files = [ + {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.13.2" +requires_python = ">=3.8" +summary = "Backported and Experimental Type Hints for Python 3.8+" +groups = ["docs", "lint", "test"] +files = [ + {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.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.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, +] + +[[package]] +name = "vulture" +version = "2.14" +requires_python = ">=3.8" +summary = "Find dead code" +groups = ["lint"] +dependencies = [ + "tomli>=1.1.0; python_version < \"3.11\"", +] +files = [ + {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.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.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 new file mode 100644 index 0000000..dfce746 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,303 @@ +[build-system] +requires = ["pdm-backend"] +build-backend = "pdm.backend" + +[project] +name = "getmac" +dynamic = ["version"] +description = "Platform-independent pure-Python module to get a MAC address" +dependencies = [] +readme = "README.md" +authors = [ + {name = "Christopher Goes", email = "ghostofgoes@gmail.com"}, +] +maintainers = [ + {name = "Christopher Goes", email = "ghostofgoes@gmail.com"}, +] +license = { file = "LICENSE"} +requires-python = "<4.0,>=3.8" +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.8", + "Programming Language :: Python :: 3.9", + "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", + "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", + "network", + "networking", + "layer2", + "layer-2", + "802.3", +] + +[project.urls] +"Homepage" = "https://github.com/GhostofGoes/getmac" +"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" +"Discord server" = "https://discord.gg/python" + +[project.scripts] +getmac = "getmac.__main__:main" + + +[tool.pdm.dev-dependencies] +test = [ + "pytest<8,>=7", + "pytest-randomly", + "pytest-mock", + "pytest-benchmark", + "pytest-cov", + "pytest-xdist", + "pytest-sugar", + "psutil", + "pygments<3.0,>=2.13", +] +lint = [ + "codespell>=2.2.4", + "tomli; python_version < \"3.12\"", + "ruff", + "vulture", + "mypy<2.0.0,>=1.3.0", +] +docs = [ + "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\"", + "sphinx-issues; python_version >= \"3.10\"", + "myst-parser; python_version >= \"3.10\"", +] + +[tool.pdm.build] +includes = ["getmac/", "LICENSE", "README.md"] +source-includes = [ + "tests/", + "CHANGELOG.md", + "CONTRIBUTING.md", + "SECURITY.md", + "pyproject.toml", +] + +[tool.pdm.version] +source = "file" +path = "getmac/getmac.py" + + +# --- PDM scripts for developers --- +[tool.pdm.scripts.spelling] +help = "Check spelling using Codespell" +cmd = "codespell --toml pyproject.toml" + +[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" + +[tool.pdm.scripts.check_format] +help = "Check code formatting is correct" +composite = [ + "ruff check --select I getmac tests", + "ruff format --check getmac tests", +] + +[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", + "unused", + "check_format", + "typing", +] + +[tool.pdm.scripts.format] +help = "Auto-format code to match requirements" +composite = [ + "ruff check --select I --fix getmac tests", + "ruff format getmac tests", +] + +[tool.pdm.scripts.benchmark] +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 +""" + +[tool.pdm.scripts.html] +help = "Build HTML documentation using Sphinx" +cmd = "sphinx-build -j auto -b html ./docs ./html_docs" + +[tool.pdm.scripts.manpage] +help = "Build Manpage documentation using Sphinx" +cmd = "sphinx-build -j auto -b man ./docs ./manpage" + +[tool.pdm.scripts.docs] +help = "Build all documentation formats using Sphinx" +composite = [ + "html", + "manpage", +] + + + +# --- 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" + + +# --- 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,third_party' +ignore-words-list = 'ether,Expresso,expresso,Espresso,espresso' + + +# --- Ruff configuration --- +# https://docs.astral.sh/ruff/ +[tool.ruff] +line-length = 99 +# 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. +# 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 = [ + "ANN", # flake8-annotations + "S", # flake8-bandit + "B", # flake8-bugbear + "A", # flake8-builtins + "C4", # flake8-comprehensions + "I", # isort + "DTZ", # flake8-datetimez + "T10", # flake8-debugger + "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 + "N", # pep8-naming + "E", # pycodestyle - Error + "W", # pycodestyle - Warning + "F", # Pyflakes + "PGH", # pygrep-hooks + "PLC", # Pylint - Convention + "PLE", # Pylint - Errors + "PLR", # Pylint - Refactor + "PLW", # Pylint - Warnings + "UP", # pyupgrade + "RUF", # Ruff-specific rules +] + +# Linting error codes to ignore +ignore = [ + "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 + "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", + "UP031", +] + +[tool.ruff.lint.per-file-ignores] +"tests/*.py" = ["ANN001", "ANN002", "ANN003", "ANN201", "ANN202", "S101", "S104", "S602", "S603", "D", "PLR"] + +[tool.ruff.format] +docstring-code-format = true +line-ending = "lf" +quote-style = "double" 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 deleted file mode 100644 index 938d7e7..0000000 --- a/setup.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import sys -from io import open # Python 2 compatibility - -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": ["getmac2 = getmac.__main__:main"]} - if sys.version_info[:2] <= (2, 7) - else {"console_scripts": ["getmac = getmac.__main__:main"]}, - install_requires=[], - python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", - keywords=[ - "getmac", - "get-mac", - "macaddress", - "mac-address", - "mac", - "ethernet", - "mac-48", - "networking", - "network", - "networking", - "layer2", - "layer-2", - "802.3", - ], - classifiers=[ # TODO(python3): update 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 :: 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 :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", - "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", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Software Development :: Libraries", - "Topic :: System :: Systems Administration", - "Topic :: System :: Networking", - "Topic :: Utilities", - ], -) diff --git a/tests/conftest.py b/tests/conftest.py index 270dd70..e9a0a39 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,21 +1,17 @@ -# -*- coding: utf-8 -*- -import io from os import path import pytest -import getmac +from getmac.variables import settings -getmac.getmac.DEBUG = 4 +settings.DEBUG = 4 -@pytest.fixture() +@pytest.fixture 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 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 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 diff --git a/tests/style-requirements.txt b/tests/style-requirements.txt deleted file mode 100644 index 4d88e8e..0000000 --- a/tests/style-requirements.txt +++ /dev/null @@ -1,25 +0,0 @@ -check-manifest==0.49 -codespell==2.2.2 -flake8>=5.0.0 -flake8-bugbear==23.1.20;python_version>='3.5' -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 -# flake8-annotations 3.0.0 removes support for PEP-484 type comments -flake8-annotations<3.0.0 -flake8-black;python_version>='3.6' -# 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' -vulture==2.7 diff --git a/tests/test-requirements.txt b/tests/test-requirements.txt deleted file mode 100644 index 0963ffd..0000000 --- a/tests/test-requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -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 diff --git a/tests/test_cli.py b/tests/test_cli.py index cfbe0ef..9c4a012 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,21 +1,16 @@ -# -*- coding: utf-8 -*- - import platform import sys from subprocess import PIPE, Popen - -import pytest +from typing import List from getmac import __version__, get_mac_address -PY2 = sys.version_info[0] == 2 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(): @@ -23,57 +18,55 @@ 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 "usage: getmac" in 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"]) + assert run_cmd([*BASE_CMD, "--version"]).strip().endswith(__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"]) - != "" - ) + 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"]) diff --git a/tests/test_getmac.py b/tests/test_getmac.py index d43dc14..ea38ea0 100644 --- a/tests/test_getmac.py +++ b/tests/test_getmac.py @@ -1,15 +1,18 @@ -# -*- coding: utf-8 -*- - import inspect -import sys +from ipaddress import ( + IPv4Address, + IPv4Interface, + IPv4Network, + IPv6Address, + IPv6Interface, + IPv6Network, +) from subprocess import CalledProcessError import pytest -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})" +from getmac import get_mac_address, getmac, utils +from getmac.variables import consts, gvars, settings def test_all_methods_defined_are_in_methods_list(): @@ -33,75 +36,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("getmac.getmac.PATH", []) - m = mocker.patch("getmac.getmac._call_proc", return_value="SUCCESS") - assert getmac._popen("TESTCMD", "ARGS") == "SUCCESS" + mocker.patch.object(gvars, "PATH", []) + 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("getmac.getmac.ENV", "ENV") - - 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") - - mocker.patch("getmac.getmac.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) - if PY2: - mocker.patch("__builtin__.open", mock_open) - else: - 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") - 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") @@ -124,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") @@ -140,20 +82,27 @@ 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") + 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] 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("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) @@ -165,11 +114,12 @@ 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") - with pytest.warns(RuntimeWarning): - assert not getmac.initialize_method_cache("invalid_method_type") - with pytest.warns(RuntimeWarning): - assert not getmac.initialize_method_cache("ip") + mocker.patch.object(consts, "PLATFORM", "linux") + + 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): @@ -179,12 +129,13 @@ 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("getmac.getmac.check_command", return_value=True) + 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 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) @@ -194,11 +145,12 @@ 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("getmac.getmac.check_command", return_value=True) - mocker.patch("getmac.getmac.check_path", return_value=True) + 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 getmac.PLATFORM == "linux" + assert consts.PLATFORM == "linux" assert isinstance(getmac.METHOD_CACHE["ip4"], getmac.ArpFile) @@ -215,24 +167,22 @@ 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) - assert ( - getmac.get_by_method("ip6", "fe80::8c8f:aaff:fec9:d28b") == "8e:8f:aa:c9:d2:8b" - ) + 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" @@ -258,29 +208,31 @@ def test_get_by_method_errors(mocker): "default_iface": None, }, ) - mocker.patch("getmac.getmac._read_file", return_value="") - mocker.patch("getmac.getmac.DEBUG", 1) + mocker.patch("getmac.utils.read_file", return_value="") + 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("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.getmac.FORCE_METHOD", "SysIfaceFile") + 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" 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" @@ -291,12 +243,38 @@ 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) @@ -309,6 +287,19 @@ 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) @@ -320,22 +311,145 @@ 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("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") 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") 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") + + +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")) + + 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()) + + +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", + { + "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" diff --git a/tests/test_methods.py b/tests/test_methods.py index bb9a4c1..5d84325 100644 --- a/tests/test_methods.py +++ b/tests/test_methods.py @@ -1,14 +1,11 @@ -# -*- coding: utf-8 -*- - import platform import socket -import sys -import uuid from subprocess import CalledProcessError 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 @@ -19,19 +16,17 @@ 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) - assert "08:00:27:2b:c2:ed" == benchmark( - getmac.DarwinNetworksetupIface().get, arg="en0" - ) + 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) - assert getmac.DarwinNetworksetupIface().test() is True - getmac.check_command.assert_called_once_with("networksetup") + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.DarwinNetworksetupIface().test() is False + utils.check_command.assert_called_once_with("networksetup") ifconfigether_samples = [ @@ -44,7 +39,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": @@ -60,6 +55,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"), @@ -115,24 +119,29 @@ 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): +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.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 @@ -143,7 +152,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") @@ -153,7 +162,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,21 +170,49 @@ 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) + + # 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.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") @@ -185,47 +222,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) - assert getmac.ArpExe().test() is True - getmac.check_command.assert_called_once_with("arp.exe") + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.ArpExe().test() is False + 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) - assert getmac.DefaultIfaceOpenBsd().test() is True - getmac.check_command.assert_called_once_with("route") + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.DefaultIfaceOpenBsd().test() is False + 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) - assert getmac.ArpOpenbsd().test() is True - getmac.check_command.assert_called_once_with("arp") + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.ArpOpenbsd().test() is False + 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) - assert getmac.DefaultIfaceFreeBsd().test() is True - getmac.check_command.assert_called_once_with("netstat") + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.DefaultIfaceFreeBsd().test() is False + utils.check_command.assert_called_once_with("netstat") @pytest.mark.parametrize( @@ -238,7 +275,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("") @@ -247,27 +284,47 @@ 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) - assert getmac.ArpFreebsd().test() is True - getmac.check_command.assert_called_once_with("arp") + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.ArpFreebsd().test() is False + utils.check_command.assert_called_once_with("arp") @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): 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") @@ -275,10 +332,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) @@ -290,22 +347,44 @@ 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_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.getmac._popen", return_value=content) - assert mac == benchmark(getmac.IpNeighborShow().get, arg=ip) + mocker.patch("getmac.utils.popen", return_value=content) + 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"), [ @@ -318,7 +397,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 @@ -334,19 +413,17 @@ 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) - assert getmac.NetstatIface().get("eth0") is None - mocker.patch("getmac.getmac._popen", return_value=" ") - assert getmac.NetstatIface().get("eth0") 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") -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]) - except_method = getmac.IpLinkIface() - assert "74:d4:35:e9:45:71" == except_method.get("eth0") + 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 @pytest.mark.parametrize( @@ -365,7 +442,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) @@ -382,7 +459,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 @@ -396,6 +473,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"), [ @@ -404,17 +495,15 @@ def test_iplinkiface_samples(benchmark, mocker, get_sample, mac, iface, sample_f ("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.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() @@ -426,19 +515,22 @@ 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.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) + # 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 - mocker.patch("getmac.getmac._read_file", return_value="") + mocker.patch("getmac.utils.read_file", return_value="") assert getmac.DefaultIfaceLinuxRouteFile().get() is None @@ -452,18 +544,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() @@ -474,26 +566,24 @@ 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.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) - assert getmac.DefaultIfaceRouteGetCommand().test() is True - getmac.check_command.assert_called_once_with("route") + mocker.patch("getmac.utils.check_command", return_value=False) + assert getmac.DefaultIfaceRouteGetCommand().test() is False + utils.check_command.assert_called_once_with("route") @pytest.mark.parametrize( @@ -507,16 +597,17 @@ 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.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 @@ -527,18 +618,40 @@ 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_arp_various_args_edge_cases(mocker, get_sample): + assert not getmac.ArpVariousArgs().get("") + + 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.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 + 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", @@ -555,31 +668,71 @@ 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", +@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_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 +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) -@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.getmac.check_command", return_value=True) - assert getmac.UuidLanscan().test() is True - getmac.check_command.assert_called_once_with("lanscan") + assert not getmac.LanscanIface().get("lo0") + 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") 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" + ) diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..56271fd --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,61 @@ +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("subprocess.DEVNULL", "DEVNULL") + mocker.patch.object(gvars, "ENV", "ENV") + + mocker.patch.object(consts, "WINDOWS", True) + 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("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") + + +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" diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 8710a42..0000000 --- a/tox.ini +++ /dev/null @@ -1,84 +0,0 @@ -[tox] -envlist = py{37,38,39,310,311,312,py}-{linux,macos,windows},ironpython-windows,pypy3-{linux,macos,windows} -skip_missing_interpreters = true - -[gh-actions] -python = - 3.7: py37 - 3.8: py38 - 3.9: py39 - 3.10: py310 - 3.11: py311 - 3.12: py312 - pypy-3.7: pypy3 - pypy-3.8: pypy3 - pypy-3.9: pypy3 - -[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 -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} - -[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} - -[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 = 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 - 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 - 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 getmac/shutilwhich.py -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 - -[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