Repository: netaddr/netaddr

Issue:
OUI hashability
# Summary
`OUI` does not have a `__hash__` function.  This prevents the use of `OUI` for keys.
- Python Hash documentation [link](https://docs.python.org/3/reference/datamodel.html#object.__hash__)

# Example - Break
```
from netaddr import OUI

a = OUI("002272")

try:
    set(a)
except TypeError:
    print("Hash failed")
```

# Example - Fix
```
from netaddr import OUI

a = OUI("002272")
set(a)
```

Discussion:
codecov-io: # [Codecov](https://codecov.io/gh/netaddr/netaddr/pull/225?src=pr&el=h1) Report
> Merging [#225](https://codecov.io/gh/netaddr/netaddr/pull/225?src=pr&el=desc) (0b6e563) into [master](https://codecov.io/gh/netaddr/netaddr/commit/606a44b62ea7032f63e359aaaaabc0057e168890?el=desc) (606a44b) will **increase** coverage by `1.27%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/netaddr/netaddr/pull/225/graphs/tree.svg?width=650&height=150&src=pr&token=QeH3hUH4xM)](https://codecov.io/gh/netaddr/netaddr/pull/225?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #225      +/-   ##
==========================================
+ Coverage   86.74%   88.02%   +1.27%     
==========================================
  Files          48       44       -4     
  Lines        4558     3190    -1368     
  Branches      659      335     -324     
==========================================
- Hits         3954     2808    -1146     
+ Misses        424      308     -116     
+ Partials      180       74     -106     
```


| [Impacted Files](https://codecov.io/gh/netaddr/netaddr/pull/225?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [netaddr/tests/eui/test\_eui.py](https://codecov.io/gh/netaddr/netaddr/pull/225/diff?src=pr&el=tree#diff-bmV0YWRkci90ZXN0cy9ldWkvdGVzdF9ldWkucHk=) | `99.53% <100.00%> (-0.47%)` | :arrow_down: |
| [netaddr/compat.py](https://codecov.io/gh/netaddr/netaddr/pull/225/diff?src=pr&el=tree#diff-bmV0YWRkci9jb21wYXQucHk=) | `51.06% <0.00%> (-40.43%)` | :arrow_down: |
| [netaddr/fbsocket.py](https://codecov.io/gh/netaddr/netaddr/pull/225/diff?src=pr&el=tree#diff-bmV0YWRkci9mYnNvY2tldC5weQ==) | `40.00% <0.00%> (-39.32%)` | :arrow_down: |
| [netaddr/tests/eui/test\_ieee\_parsers.py](https://codecov.io/gh/netaddr/netaddr/pull/225/diff?src=pr&el=tree#diff-bmV0YWRkci90ZXN0cy9ldWkvdGVzdF9pZWVlX3BhcnNlcnMucHk=) | `62.16% <0.00%> (-37.84%)` | :arrow_down: |
| [netaddr/tests/ip/test\_platform\_osx.py](https://codecov.io/gh/netaddr/netaddr/pull/225/diff?src=pr&el=tree#diff-bmV0YWRkci90ZXN0cy9pcC90ZXN0X3BsYXRmb3JtX29zeC5weQ==) | `60.00% <0.00%> (-28.24%)` | :arrow_down: |
| [netaddr/tests/strategy/test\_eui48\_strategy.py](https://codecov.io/gh/netaddr/netaddr/pull/225/diff?src=pr&el=tree#diff-bmV0YWRkci90ZXN0cy9zdHJhdGVneS90ZXN0X2V1aTQ4X3N0cmF0ZWd5LnB5) | `90.00% <0.00%> (-10.00%)` | :arrow_down: |
| [netaddr/tests/strategy/test\_ipv6\_strategy.py](https://codecov.io/gh/netaddr/netaddr/pull/225/diff?src=pr&el=tree#diff-bmV0YWRkci90ZXN0cy9zdHJhdGVneS90ZXN0X2lwdjZfc3RyYXRlZ3kucHk=) | `91.30% <0.00%> (-8.70%)` | :arrow_down: |
| [netaddr/tests/core/test\_compat.py](https://codecov.io/gh/netaddr/netaddr/pull/225/diff?src=pr&el=tree#diff-bmV0YWRkci90ZXN0cy9jb3JlL3Rlc3RfY29tcGF0LnB5) | `88.00% <0.00%> (-8.16%)` | :arrow_down: |
| [netaddr/tests/strategy/test\_ipv4\_strategy.py](https://codecov.io/gh/netaddr/netaddr/pull/225/diff?src=pr&el=tree#diff-bmV0YWRkci90ZXN0cy9zdHJhdGVneS90ZXN0X2lwdjRfc3RyYXRlZ3kucHk=) | `92.30% <0.00%> (-7.70%)` | :arrow_down: |
| [netaddr/tests/ip/test\_ip\_v6.py](https://codecov.io/gh/netaddr/netaddr/pull/225/diff?src=pr&el=tree#diff-bmV0YWRkci90ZXN0cy9pcC90ZXN0X2lwX3Y2LnB5) | `97.77% <0.00%> (-2.23%)` | :arrow_down: |
| ... and [9 more](https://codecov.io/gh/netaddr/netaddr/pull/225/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/netaddr/netaddr/pull/225?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/netaddr/netaddr/pull/225?src=pr&el=footer). Last update [606a44b...0b6e563](https://codecov.io/gh/netaddr/netaddr/pull/225?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/netaddr/eui/__init__.py b/netaddr/eui/__init__.py
index 5639676..07bbdc3 100644
--- a/netaddr/eui/__init__.py
+++ b/netaddr/eui/__init__.py
@@ -100,6 +100,9 @@ class OUI(BaseIdentifier):
         else:
             raise NotRegisteredError('OUI %r not registered!' % (oui,))
 
+    def __hash__(self):
+        return hash(self._value)
+
     def __eq__(self, other):
         if not isinstance(other, OUI):
             try:


Test patch:
diff --git a/netaddr/tests/eui/test_eui.py b/netaddr/tests/eui/test_eui.py
index 645a518..c17d0ce 100644
--- a/netaddr/tests/eui/test_eui.py
+++ b/netaddr/tests/eui/test_eui.py
@@ -186,6 +186,14 @@ def test_oui_constructor():
     assert oui.reg_count == 3
 
 
+def test_oui_hash():
+    oui0 = OUI(0)
+    oui1 = OUI(1)
+    oui_dict = {oui0: None, oui1: None}
+
+    assert list(oui_dict.keys()) == [OUI(0), OUI(1)]
+
+
 def test_eui_iab():
     mac = EUI('00-50-C2-00-0F-01')
     assert mac.is_iab()