Skip to content

Commit

Permalink
Document rewrite changes
Browse files Browse the repository at this point in the history
  • Loading branch information
GhostofGoes committed May 12, 2022
1 parent 1a5c774 commit 5a58ef3
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 6 deletions.
31 changes: 30 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions docs/TODO.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
9 changes: 7 additions & 2 deletions docs/releasing.md
Original file line number Diff line number Diff line change
@@ -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`)
Expand Down Expand Up @@ -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
12. Announce the release in the normal places
25 changes: 25 additions & 0 deletions docs/rewrite.md
Original file line number Diff line number Diff line change
@@ -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).
2 changes: 1 addition & 1 deletion getmac/getmac.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 5a58ef3

Please sign in to comment.