Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* add counties, thanks to [Ray Kiddy](https://github.com/rkiddy)
* add `clean_name()` method and `fallback_func` parameter to `lookup()` to provide customizable matching, thanks to [Max Filenko](https://github.com/mfilenko) and [Charlie Tonneslan](https://github.com/c-tonneslan)
* DC has returned to `STATES_AND_TERRITORIES`, thanks to [Kavi Gupta](https://github.com/kavigupta)
* add `us.states.enumeration()` for dynamic `Enum` construction from `State` attributes
* add `us.__version__` and deprecate `us.version`
* fix `py.typed` location, thanks to [johnw-bluemark](https://github.com/johnw-bluemark)
* add support for Python 3.13 and 3.14
Expand Down
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,34 @@ additional states argument:
```


### Enumerations

The `enumeration()` method dynamically constructs an `Enum` of states, keyed
by state abbreviation. The value of each member is taken from the attribute
named by `value_field`, which defaults to `name`.

```python
>>> States = us.states.enumeration()
>>> States.VA
<States.VA: 'Virginia'>
>>> States.VA.value
'Virginia'

>>> States = us.states.enumeration('fips')
>>> States.CA.value
'06'
```

Like `mapping()`, this method uses `us.STATES_AND_TERRITORIES` as the
default state list, which can be overridden by passing a `states` argument:

```python
>>> States = us.states.enumeration('fips', states=[us.states.DC, us.states.MD])
>>> list(States)
[<States.DC: '11'>, <States.MD: '24'>]
```


### DC should be granted statehood

Washington, DC does not appear in `us.STATES` or any of the
Expand Down
9 changes: 8 additions & 1 deletion us/states.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import re
from typing import Any, Callable, Dict, Iterable, List, Optional
from enum import Enum
from typing import Any, Callable, Dict, Iterable, List, Optional, Type
from urllib.parse import urljoin

import jellyfish # type: ignore
Expand Down Expand Up @@ -203,6 +204,12 @@ def mapping(from_field: str, to_field: str, states: Optional[Iterable[State]] =
return {getattr(s, from_field): getattr(s, to_field) for s in states}


def enumeration(value_field: str = "name", states: Optional[Iterable[State]] = None) -> Type[Enum]:
if states is None:
states = STATES_AND_TERRITORIES
return Enum("States", {s.abbr: getattr(s, value_field) for s in states})


AL = State(
**{
"fips": "01",
Expand Down
28 changes: 28 additions & 0 deletions us/tests/test_us.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,34 @@ def test_custom_mapping():
assert "MD" in mapping


# enumerations


def test_enumeration():
states = us.STATES[:5]
enum = us.states.enumeration("name", states=states)
for state in states:
assert enum[state.abbr].value == state.name


def test_enumeration_default_states():
enum = us.states.enumeration("name")
assert enum["VA"].value == "Virginia"
assert enum["DC"].value == "District of Columbia"


def test_enumeration_default_value_field():
enum = us.states.enumeration()
assert enum["VA"].value == "Virginia"


def test_custom_enumeration():
enum = us.states.enumeration("fips", states=[us.states.DC, us.states.MD])
assert len(enum) == 2
assert enum["DC"].value == us.states.DC.fips
assert enum["MD"].value == us.states.MD.fips


# known bugs


Expand Down