Skip to content

Add a vintage argument to shapefile_urls(), with the default staying 2010 until 5.0 - #105

Open
nickeubank wants to merge 1 commit into
unitedstates:mainfrom
nickeubank:shapefiles-2020
Open

nickeubank wants to merge 1 commit into
unitedstates:mainfrom
nickeubank:shapefiles-2020

Conversation

@nickeubank

@nickeubank nickeubank commented Oct 7, 2026 •

Copy link
Copy Markdown

shapefile_urls() has been pinned to the 2010 TIGER/Line files since it was written. This adds a vintage argument so callers can ask for the 2020 vintage, without changing what existing callers get back:

>>> us.states.MD.shapefile_urls(vintage=2010)['block']
'https://www2.census.gov/geo/tiger/TIGER2010/TABBLOCK/2010/tl_2010_24_tabblock10.zip'
>>> us.states.MD.shapefile_urls(vintage=2020)['block']
'https://www2.census.gov/geo/tiger/TIGER2020/TABBLOCK20/tl_2020_24_tabblock20.zip'

An unsupported vintage raises ValueError. States without a FIPS code still return None.

The default stays 2010 for now

This PR originally flipped the default to 2020. That's a breaking change, so the default stays at 2010 and the switch is only announced. Calling shapefile_urls() with no vintage returns the 2010 URLs exactly as before, and raises a DeprecationWarning:

shapefile_urls() currently defaults to the 2010 TIGER/Line vintage, but will default to the 2020 vintage in us 5.0. Pass vintage=2010 to keep the current URLs, or vintage=2020 to opt in to the new default now.

Passing either vintage explicitly opts out of the warning. The warning uses stacklevel=2 so it's attributed to the caller, matching the existing us.version deprecation. Flipping the default in 5.0 is then a two-line change: DEFAULT_SHAPEFILE_VINTAGE = 2020 and dropping the warning.

The states CLI passes vintage=DEFAULT_SHAPEFILE_VINTAGE explicitly, so it prints 2010 URLs today, picks up the new default automatically in 5.0, and never shows a warning its users can't act on.

The 2020 layout isn't a drop-in path swap

Two things changed on the Census side that are worth a look, since they affect what vintage=2020 callers get back:

  1. Several layers are nationwide-only in 2020. https://www2.census.gov/geo/tiger/TIGER2020/ has exactly one zip each in CD/, COUNTY/, STATE/ and ZCTA520/ — there are no per-state files any more. Rather than drop those keys and change the dict shape between vintages, they point at the nationwide file, which is identical for every state. Per-state files remain for tract, blockgroup and block (56 each, matching this package's 50 states + DC + 5 territories exactly).

  2. TABBLOCK vs TABBLOCK20. TIGER2020/TABBLOCK/ still holds 2010 census blocks reissued in the 2020 vintage; the 2020 blocks live in TABBLOCK20/. block uses TABBLOCK20. Likewise zcta uses ZCTA520 (2020 ZCTAs) rather than ZCTA5 (2010 ZCTAs). cd is the 116th Congress, the only one published in this vintage.

Resulting vintage=2020 URLs for MD:

region 2020 URL
tract TIGER2020/TRACT/tl_2020_24_tract.zip
blockgroup TIGER2020/BG/tl_2020_24_bg.zip
block TIGER2020/TABBLOCK20/tl_2020_24_tabblock20.zip
cd TIGER2020/CD/tl_2020_us_cd116.zip
county TIGER2020/COUNTY/tl_2020_us_county.zip
state TIGER2020/STATE/tl_2020_us_state.zip
zcta TIGER2020/ZCTA520/tl_2020_us_zcta520.zip

Happy to change the nationwide-layer handling if you'd rather those keys were omitted for 2020 — it seemed better to keep the dict shape stable, but it does mean us.states.MD.shapefile_urls(vintage=2020)['county'] == us.states.TX.shapefile_urls(vintage=2020)['county'].

Tests

The permanently skipped test_head is replaced with:

  • Offline tests (always run): the default is 2010 and matches an explicit vintage=2010; the no-argument call raises exactly one DeprecationWarning whose message names both vintages, the 5.0 release and both vintage= spellings, and which is attributed to the calling file (so the stacklevel is right); passing a vintage explicitly raises nothing under simplefilter("error", DeprecationWarning); exact URLs for both vintages; key coverage across all 56 states and territories; None for FIPS-less obsolete states; ValueError on a bad vintage.
  • network-marked tests: HEAD every generated URL against the Census Bureau for both vintages. These use urllib from the stdlib, so no new dependency — requests stays undeclared.

The network tests are deselected by default via addopts = "-m 'not network'", since they need network access and make a few hundred requests. Run them with:

uv run pytest -m network

I ran them while preparing this: all 172 distinct 2020 URLs and all 392 distinct 2010 URLs return 200. Verified on Python 3.8 and 3.14; ruff check, ruff format --check and ty check are clean.

README and CHANGELOG are updated to match.

🤖 Generated with Claude Code

@jcarbaugh

Copy link
Copy Markdown
Member

@nickeubank this is a great change, thanks for working through it! Since it is a breaking change I'd want to wait until a 5.0 release and since we just did 4.0 I'm hesitant to jump up to that yet. If the default still pulled from the 2010 files and 2020 could be passed as vintage that would keep existing behavior and I could push this out as a 4.1 release. What do you think?

@nickeubank

Copy link
Copy Markdown
Author

makes sense to me! Let me put together an update.

@nickeubank

Copy link
Copy Markdown
Author

Updating and adding a depreciation warning.

How would you feel about adding a condition to up the default vintage to 2020 when the version passes 5.0?

@nickeubank nickeubank changed the title Default shapefile URLs to the 2020 TIGER/Line vintage Add a vintage argument to shapefile_urls(), with the default staying 2010 until 5.0 Oct 8, 2026
`shapefile_urls()` has been pinned to the 2010 TIGER/Line files since it
was written. Add a `vintage` argument so callers can ask for the 2020
vintage instead; an unsupported vintage raises ValueError, and states
without a FIPS code still return None.

The default stays 2010, since changing it outright would break existing
callers. Calling without a `vintage` raises a DeprecationWarning saying
the default becomes 2020 in us 5.0; passing either vintage explicitly
opts out of the warning. The states CLI passes the default explicitly so
it tracks the library default without warning its own users.

The 2020 vintage isn't a drop-in path swap: the Census Bureau only
publishes nationwide files for the cd, county, state and zcta layers, so
those URLs are the same for every state. 2020 census blocks live in
TABBLOCK20 (TIGER2020/TABBLOCK holds reissued 2010 blocks) and 2020
ZCTAs in ZCTA520; cd is the 116th Congress, the only one in this vintage.

Replace the permanently skipped test_head with offline tests covering
the default vintage, the deprecation warning, the exact URLs for both
vintages, and all 56 states and territories, plus `network`-marked tests
that HEAD every generated URL against the Census Bureau. Those use
urllib rather than requests, so no new dependency, and are deselected by
default since they make a few hundred requests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants