diff --git a/CHANGELOG.md b/CHANGELOG.md index e354973..656887a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,37 @@ **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). -## 0.9.0a0 (TBD) +## 0.9.0a1 (TBD) +**Announcement**: Compatibility with Python versions older than 3.6 (2.7, 3.4, and 3.5) is deprecated and will be removed in getmac 1.0.0. If you are stuck on an unsupported Python, considor loosely pinning the version of this package in your dependency list, e.g. `getmac<1`. + +**NOTE**: This release is a *complete rewrite of getmac from the ground up*. It's passing tests and seems to be operable. However, with a change this large there are ineviteably issues that the tests or I don't catch, so I'm doing a series of pre-releases until I'm 99% confident in it's stability. Refer to `docs/rewrite.md` for a in-depth explanation of the rewrite changes. + +The new system has a number of benefits +- Reduction of false-positives and false-negatives by improving method selection accuracy (platform, validity, etc.) +- *Significantly* faster overall +- "Misses" have the same performance as "Hits" +- Easier to test, since each method can be tested directly via it's class +- Eaiser to type annotate and analyze with mypy +- Easier to read, improving reviewability and ease of contributing for newcomers +- Extensible! Custom methods can be defined and added at runtime (which is perfect if you have some particular edge case but aren't able to open-source it). + +### Added +* Support Python 3.9 + +### Changed +* **Complete rewrite of `getmac` from the ground up. Refer to `docs/rewrite.md` for a in-depth explanation of the rewrite changes** * Fixed a failure to look up a hostname now returns `None`, as expected, instead of raising an exception (`socket.gaierror`). +* Fixed numerous false-negative and false-positive bugs +* Improved performance overall +* Performance for cases where no MAC is found is now the same as cases where a MAC is found (speed of "misses" now equals that of "hits") +* Improved the reliability of many methods +* Improved the performance of many methods + +### Dev +* Add samples and tests for WSL (Ubuntu 18.04) +* Add flake8 plugins: `flake8-pytest-style` and `flake8-annotations` +* Add additional tests +* Improve existing tests ## 0.8.3 (12/10/2021) diff --git a/docs/TODO.md b/docs/TODO.md index 16106ad..a66aefb 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -1,5 +1,5 @@ # 0.9.0 release -- [ ] Tag Beta pre-release on PyPI +- [x] Tag Beta pre-release on PyPI - [ ] GitHub Actions for CI - [ ] Remove TravisCI - [ ] Remove Appveyor @@ -19,11 +19,12 @@ - [ ] Add ability to set the platform used (and document this) via - argument to `get_mac_address()` - CLI argument -- [ ] Add changelog and other modern PyPI page fields to getmac setup.py +- [x] Add changelog and other modern PyPI page fields to getmac setup.py # 1.0.0 release +- [ ] Switch to Poetry for project management - [ ] 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 diff --git a/docs/releasing.md b/docs/releasing.md index 35423dc..401eff0 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -1,6 +1,11 @@ -Dependencies: `pip install twine wheel stdeb` +## Requirements +```bash +python -m pip install -U pip +pip install -U setuptools twine wheel 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`) @@ -33,4 +38,4 @@ python setup.py --command-packages=stdeb.command bdist_deb 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 \ No newline at end of file +12. Announce the release in the normal places diff --git a/docs/rewrite.md b/docs/rewrite.md new file mode 100644 index 0000000..4517270 --- /dev/null +++ b/docs/rewrite.md @@ -0,0 +1,25 @@ +# Rewrite summary + +The current system of finding a MAC is, to put it bluntly, throw commands at the wall, see if they stick, and promptly forget what stuck for next time. While this *has* worked up till now, it's a hack built on hacks and has needed a rewrite for a while. It's prone to false-positives (multiple nasty bugs were caused by this), is quite slow ("misses" can take *seconds* to return!), extremely difficult to test (and thus aformentioned bugs were missed), and is generally a unreadable pile of spaghetti to anyone except me. + +The rewrite is built from the ground up as a class-based modular architecture. Each "method" (a way of getting a MAC) is implemented as a subclass of the `Method` base class. The methods define what platforms they apply to (`platforms`, e.g. `platforms = {"windows", "wsl"}`), the type of method (`method_type`, e.g. `method_type = "ip4"`) and other attributes, such as if they make a network request as part of the check (`net_request`). + +There are two functions that are implemented by `Method` subclasses: `test()` and `get(arg)`. The `test()` functions implements a *fast* test for the feasibility of the method, e.g. checking if the `/proc/net/arp` file exists for the `ArpFile` method. The `get(arg)` functions implements the actual functionality of looking up the MAC, e.g. in the case of `ArpFile`, parsing the contents of `/proc/net/arp`. + +When `get_mac_address()` is called for the first time for a particular method type (e.g. `"iface"`), a cache is initialized for that method type (in `initialize_method_cache()`): +1. Create a list of all methods +2. Remove any that don't apply to the current platform (e.g. `"windows"`) +3. Remove any that don't apply to this method type (`"iface"`) +4. Test all methods by calling `test()` and remove any that fail (return `False`) +5. Store any methods that remain in the cache for this method type (`"iface"`) + +The first of the methods in the cache is used to fulfill the `get_mac_address()` via a call to `get(arg)` on the method. If there's a critical failure during the `get()`, then the method is marked as unusable, removed from the cache, and the next method in the cache is used instead. Some methods can't be tested reliably without starting a process, which is expensive, so instead we fail them on first attempt. Calling a single method addresses the old system's issue of trying every method until there was a success, which led to "misses" (no MAC available for whatever was requested) taking several seconds (or longer in extreme cases). + +The new system has a number of benefits +- Reduction of false-positives and false-negatives by improving method selection accuracy (platform, validity, etc.) +- *Significantly* faster overall +- "Misses" have the same performance as "Hits" +- Easier to test, since each method can be tested directly via it's class +- Eaiser to type annotate and analyze with mypy +- Easier to read, improving reviewability and ease of contributing for newcomers +- Extensible! Custom methods can be defined and added at runtime (which is perfect if you have some particular edge case but aren't able to open-source it). diff --git a/getmac/getmac.py b/getmac/getmac.py index 8e44ff5..e47ca62 100644 --- a/getmac/getmac.py +++ b/getmac/getmac.py @@ -55,7 +55,7 @@ if not log.handlers: log.addHandler(logging.NullHandler()) -__version__ = "0.9.0a0" +__version__ = "0.9.0a1" PY2 = sys.version_info[0] == 2 # type: bool diff --git a/setup.py b/setup.py index 5135ac2..aee948b 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,7 @@ "Documentation": "https://getmac.readthedocs.io/en/latest/", "Changelog": "https://github.com/GhostofGoes/getmac/blob/master/CHANGELOG.md", "Issue tracker": "https://github.com/GhostofGoes/getmac/issues", + "Source": "https://github.com/GhostofGoes/getmac", "Discord server": "https://discord.gg/python", }, license="MIT",