From 23f5a7f0e60d981d11f42fa12880ce52222ee492 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 24 Jan 2026 10:44:30 -0500 Subject: [PATCH 1/6] Add pandas 3.0 compatibility tests Tests verify that policyengine-us works with pandas 3.0 changes: - State-based parameter lookups (StringArray handling) - String-typed variables (filled_array with StringDtype) - Enum variables These tests require policyengine-core >= 3.9.1 which has the pandas 3 fixes. Co-Authored-By: Claude Opus 4.5 --- changelog_entry.yaml | 4 + .../tests/core/test_pandas3_compatibility.py | 137 ++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 policyengine_us/tests/core/test_pandas3_compatibility.py diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb2d..3078c78bb3d 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: patch + changes: + added: + - Added pandas 3.0 compatibility tests to verify state parameter lookups and string variables work correctly diff --git a/policyengine_us/tests/core/test_pandas3_compatibility.py b/policyengine_us/tests/core/test_pandas3_compatibility.py new file mode 100644 index 00000000000..dd1fb35fff5 --- /dev/null +++ b/policyengine_us/tests/core/test_pandas3_compatibility.py @@ -0,0 +1,137 @@ +""" +Tests for pandas 3.0.0 compatibility in policyengine-us. + +These tests verify that policyengine-us works correctly with pandas 3.0.0, +which introduces PyArrow-backed strings as default (StringDtype). + +These tests will FAIL if policyengine-core < 3.9.1 is used, which doesn't +have the pandas 3 compatibility fixes. +""" + +import numpy as np + +from policyengine_us import Simulation + + +class TestStateParameterLookupWithPandas3: + """ + Test that state-based parameter lookup works with pandas 3 StringArray. + + In pandas 3, string columns use StringDtype by default. When looking up + state-specific parameters using vectorial indexing (e.g., + parameters.state.rate[state_code]), the state codes may be StringArray + instead of numpy array. + + policyengine-core >= 3.9.1 converts StringArray to numpy before lookup. + """ + + def test_state_parameter_lookup(self): + """ + Test that state-based parameter lookup works for multiple states. + + This exercises the VectorialParameterNodeAtInstant.__getitem__ fix + that converts pandas StringArray to numpy array. + """ + # Create a simulation with households in different states + sim = Simulation( + situation={ + "people": { + "person1": {"age": {"2024": 30}}, + "person2": {"age": {"2024": 40}}, + }, + "households": { + "household1": { + "members": ["person1"], + "state_code": {"2024": "CA"}, + }, + "household2": { + "members": ["person2"], + "state_code": {"2024": "NY"}, + }, + }, + } + ) + + # This calculation involves state-based parameter lookups + # If pandas 3 StringArray handling is broken, this would raise: + # TypeError: unhashable type: 'StringArray' + result = sim.calculate("household_net_income", "2024") + + # Basic sanity check - should return an array + assert isinstance(result, np.ndarray) + assert len(result) == 2 # Two households + + +class TestFilledArrayWithStringDtype: + """ + Test that population.filled_array works with pandas StringDtype. + + In pandas 3, numpy.full() cannot handle StringDtype. policyengine-core + >= 3.9.1 converts StringDtype to object dtype before calling numpy.full(). + """ + + def test_string_variable_default_value(self): + """ + Test that string-typed variables work correctly. + + Variables with value_type=str use filled_array with a string dtype. + In pandas 3, this would fail with: + TypeError: Cannot interpret '' as a data type + """ + # Create a simple simulation + sim = Simulation( + situation={ + "people": { + "person1": {"age": {"2024": 30}}, + }, + "households": { + "household1": { + "members": ["person1"], + }, + }, + } + ) + + # state_code is a string variable - calculating it exercises filled_array + # with the default value + result = sim.calculate("state_code", "2024") + + # Should return valid results without error + assert len(result) == 1 + + +class TestEnumVariableWithPandas3: + """ + Test that Enum variables work correctly with pandas 3. + + Enum variables involve string-based parameter lookups which can + trigger the StringArray issue in pandas 3. + """ + + def test_filing_status_enum(self): + """ + Test that filing_status enum works correctly. + """ + sim = Simulation( + situation={ + "people": { + "person1": {"age": {"2024": 30}}, + }, + "tax_units": { + "tax_unit1": { + "members": ["person1"], + }, + }, + "households": { + "household1": { + "members": ["person1"], + }, + }, + } + ) + + # filing_status is an enum variable + result = sim.calculate("filing_status", "2024") + + # Should return valid results + assert len(result) == 1 From 70dc97ca72e289eec0c1288c2328b87b0afc0418 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 24 Jan 2026 10:47:33 -0500 Subject: [PATCH 2/6] Add weeks_unemployed variable Input variable for weeks looking for work during the year. Sourced from CPS ASEC variable LKWEEKS. Co-Authored-By: Claude Opus 4.5 --- .../variables/gov/states/weeks_unemployed.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 policyengine_us/variables/gov/states/weeks_unemployed.py diff --git a/policyengine_us/variables/gov/states/weeks_unemployed.py b/policyengine_us/variables/gov/states/weeks_unemployed.py new file mode 100644 index 00000000000..eb3de6504eb --- /dev/null +++ b/policyengine_us/variables/gov/states/weeks_unemployed.py @@ -0,0 +1,13 @@ +from policyengine_us.model_api import * + + +class weeks_unemployed(Variable): + value_type = float + entity = Person + label = "weeks unemployed" + unit = "week" + documentation = ( + "Number of weeks during the year the person was looking for work. " + "From CPS ASEC variable LKWEEKS." + ) + definition_period = YEAR From 8cc06f9587beb2aa90802daa757e775094de8cb1 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 24 Jan 2026 10:54:25 -0500 Subject: [PATCH 3/6] Enable pandas 3.0 and add explicit core compatibility tests - Remove pandas <3.0 cap to allow pandas 3.0.0 - Add tests that directly test policyengine-core's: - filled_array handling of pandas StringDtype - VectorialParameterNodeAtInstant handling of StringArray These tests will FAIL until policyengine-core >= 3.9.1 is released with the pandas 3 compatibility fixes. This demonstrates TDD - the tests catch the bug when the fix is not present. Co-Authored-By: Claude Opus 4.5 --- changelog_entry.yaml | 4 +- .../tests/core/test_pandas3_compatibility.py | 81 ++++++++++++ pyproject.toml | 2 +- uv.lock | 117 +++++++++++++++--- 4 files changed, 183 insertions(+), 21 deletions(-) diff --git a/changelog_entry.yaml b/changelog_entry.yaml index 3078c78bb3d..639b9a470ff 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -1,4 +1,6 @@ - bump: patch changes: added: - - Added pandas 3.0 compatibility tests to verify state parameter lookups and string variables work correctly + - Added pandas 3.0 compatibility tests to verify policyengine-core fixes for StringDtype and StringArray handling + changed: + - Removed pandas <3.0 version cap to enable pandas 3.0 support diff --git a/policyengine_us/tests/core/test_pandas3_compatibility.py b/policyengine_us/tests/core/test_pandas3_compatibility.py index dd1fb35fff5..6bf6c7d0d78 100644 --- a/policyengine_us/tests/core/test_pandas3_compatibility.py +++ b/policyengine_us/tests/core/test_pandas3_compatibility.py @@ -9,10 +9,91 @@ """ import numpy as np +import pandas as pd from policyengine_us import Simulation +class TestCoreFilledArrayWithStringDtype: + """ + Test policyengine-core's filled_array handles pandas StringDtype. + + This directly tests the fix in policyengine-core that converts + pandas ExtensionDtype to object dtype before calling numpy.full(). + + This test WILL FAIL with policyengine-core < 3.9.1. + """ + + def test_filled_array_with_string_dtype(self): + """ + Test that filled_array handles pandas StringDtype. + + In pandas 3, numpy.full() cannot handle StringDtype, raising: + TypeError: Cannot interpret '' as a data type + """ + from policyengine_core.populations.population import Population + from policyengine_core.entities import Entity + + entity = Entity( + key="person", + plural="people", + label="Person", + doc="Test entity", + ) + population = Population(entity) + population.count = 5 + + # Explicitly use pandas StringDtype - this is what pandas 3 uses by default + string_dtype = pd.StringDtype() + + # This will fail without the fix: + # TypeError: Cannot interpret '' as a data type + result = population.filled_array("test_value", dtype=string_dtype) + + assert len(result) == 5 + assert all(v == "test_value" for v in result) + + +class TestCoreVectorialParameterWithStringArray: + """ + Test policyengine-core's VectorialParameterNodeAtInstant handles StringArray. + + This directly tests the fix that converts pandas StringArray to numpy + before vectorial parameter lookup. + + This test WILL FAIL with policyengine-core < 3.9.1. + """ + + def test_vectorial_parameter_with_string_array(self): + """ + Test that vectorial parameter lookup handles pandas StringArray. + + In pandas 3, string operations return StringArray. Parameter lookup + would fail with: TypeError: unhashable type: 'StringArray' + """ + from policyengine_core.parameters.vectorial_parameter_node_at_instant import ( + VectorialParameterNodeAtInstant, + ) + + # Create a simple vectorial node + vector = np.array( + [(1.0, 2.0)], + dtype=[("zone_1", "float"), ("zone_2", "float")], + ).view(np.recarray) + + node = VectorialParameterNodeAtInstant("test", vector, "2024-01-01") + + # Create a pandas StringArray - this is what pandas 3 returns for string ops + key = pd.array(["zone_1", "zone_2"], dtype="string") + + # This will fail without the fix: + # TypeError: unhashable type: 'StringArray' + result = node[key] + + assert len(result) == 2 + np.testing.assert_array_equal(result, [1.0, 2.0]) + + class TestStateParameterLookupWithPandas3: """ Test that state-based parameter lookup works with pandas 3 StringArray. diff --git a/pyproject.toml b/pyproject.toml index 36f16929197..bd69d063918 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ ] dependencies = [ "microdf-python>=1.0.0", - "pandas>=2.0,<3.0", # pandas 3.0 has StringDtype incompatibility with numpy + "pandas>=2.0", "policyengine-core>=3.23.0", "tqdm>=4.67.1", ] diff --git a/uv.lock b/uv.lock index 058e9371836..7fe3a91cc29 100644 --- a/uv.lock +++ b/uv.lock @@ -2,8 +2,12 @@ version = 1 revision = 3 requires-python = ">=3.10, <3.14" resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", "python_full_version < '3.11'", ] @@ -480,8 +484,12 @@ name = "docutils" version = "0.22.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } wheels = [ @@ -718,7 +726,7 @@ name = "importlib-metadata" version = "8.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp" }, + { name = "zipp", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } wheels = [ @@ -1081,7 +1089,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pandas" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/88/9b/e41192b8bdd7e9017f5b940f29db00f926b06b93aea1e03de76c808b7c9e/microdf_python-1.1.2.tar.gz", hash = "sha256:34ce6542dbc56013de812f5355a144363f427972af45135329d43a5c85921d4b", size = 17731, upload-time = "2026-01-07T12:06:02.113Z" } wheels = [ @@ -1181,8 +1190,12 @@ name = "networkx" version = "3.6.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } wheels = [ @@ -1320,8 +1333,12 @@ name = "numpy" version = "2.4.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/24/62/ae72ff66c0f1fd959925b4c11f8c2dea61f47f6acaea75a08512cdfe3fed/numpy-2.4.1.tar.gz", hash = "sha256:a1ceafc5042451a858231588a104093474c6a5c57dcc724841f5c888d237d690", size = 20721320, upload-time = "2026-01-10T06:44:59.619Z" } wheels = [ @@ -1399,12 +1416,14 @@ wheels = [ name = "pandas" version = "2.3.3" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "pytz", marker = "python_full_version < '3.11'" }, + { name = "tzdata", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } wheels = [ @@ -1444,6 +1463,58 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, ] +[[package]] +name = "pandas" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/da/b1dc0481ab8d55d0f46e343cfe67d4551a0e14fcee52bd38ca1bd73258d8/pandas-3.0.0.tar.gz", hash = "sha256:0facf7e87d38f721f0af46fe70d97373a37701b1c09f7ed7aeeb292ade5c050f", size = 4633005, upload-time = "2026-01-21T15:52:04.726Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/1e/b184654a856e75e975a6ee95d6577b51c271cd92cb2b020c9378f53e0032/pandas-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d64ce01eb9cdca96a15266aa679ae50212ec52757c79204dbc7701a222401850", size = 10313247, upload-time = "2026-01-21T15:50:15.775Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5e/e04a547ad0f0183bf151fd7c7a477468e3b85ff2ad231c566389e6cc9587/pandas-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:613e13426069793aa1ec53bdcc3b86e8d32071daea138bbcf4fa959c9cdaa2e2", size = 9913131, upload-time = "2026-01-21T15:50:18.611Z" }, + { url = "https://files.pythonhosted.org/packages/a2/93/bb77bfa9fc2aba9f7204db807d5d3fb69832ed2854c60ba91b4c65ba9219/pandas-3.0.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0192fee1f1a8e743b464a6607858ee4b071deb0b118eb143d71c2a1d170996d5", size = 10741925, upload-time = "2026-01-21T15:50:21.058Z" }, + { url = "https://files.pythonhosted.org/packages/62/fb/89319812eb1d714bfc04b7f177895caeba8ab4a37ef6712db75ed786e2e0/pandas-3.0.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0b853319dec8d5e0c8b875374c078ef17f2269986a78168d9bd57e49bf650ae", size = 11245979, upload-time = "2026-01-21T15:50:23.413Z" }, + { url = "https://files.pythonhosted.org/packages/a9/63/684120486f541fc88da3862ed31165b3b3e12b6a1c7b93be4597bc84e26c/pandas-3.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:707a9a877a876c326ae2cb640fbdc4ef63b0a7b9e2ef55c6df9942dcee8e2af9", size = 11756337, upload-time = "2026-01-21T15:50:25.932Z" }, + { url = "https://files.pythonhosted.org/packages/39/92/7eb0ad232312b59aec61550c3c81ad0743898d10af5df7f80bc5e5065416/pandas-3.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:afd0aa3d0b5cda6e0b8ffc10dbcca3b09ef3cbcd3fe2b27364f85fdc04e1989d", size = 12325517, upload-time = "2026-01-21T15:50:27.952Z" }, + { url = "https://files.pythonhosted.org/packages/51/27/bf9436dd0a4fc3130acec0828951c7ef96a0631969613a9a35744baf27f6/pandas-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:113b4cca2614ff7e5b9fee9b6f066618fe73c5a83e99d721ffc41217b2bf57dd", size = 9881576, upload-time = "2026-01-21T15:50:30.149Z" }, + { url = "https://files.pythonhosted.org/packages/e7/2b/c618b871fce0159fd107516336e82891b404e3f340821853c2fc28c7830f/pandas-3.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c14837eba8e99a8da1527c0280bba29b0eb842f64aa94982c5e21227966e164b", size = 9140807, upload-time = "2026-01-21T15:50:32.308Z" }, + { url = "https://files.pythonhosted.org/packages/0b/38/db33686f4b5fa64d7af40d96361f6a4615b8c6c8f1b3d334eee46ae6160e/pandas-3.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9803b31f5039b3c3b10cc858c5e40054adb4b29b4d81cb2fd789f4121c8efbcd", size = 10334013, upload-time = "2026-01-21T15:50:34.771Z" }, + { url = "https://files.pythonhosted.org/packages/a5/7b/9254310594e9774906bacdd4e732415e1f86ab7dbb4b377ef9ede58cd8ec/pandas-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14c2a4099cd38a1d18ff108168ea417909b2dea3bd1ebff2ccf28ddb6a74d740", size = 9874154, upload-time = "2026-01-21T15:50:36.67Z" }, + { url = "https://files.pythonhosted.org/packages/63/d4/726c5a67a13bc66643e66d2e9ff115cead482a44fc56991d0c4014f15aaf/pandas-3.0.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d257699b9a9960e6125686098d5714ac59d05222bef7a5e6af7a7fd87c650801", size = 10384433, upload-time = "2026-01-21T15:50:39.132Z" }, + { url = "https://files.pythonhosted.org/packages/bf/2e/9211f09bedb04f9832122942de8b051804b31a39cfbad199a819bb88d9f3/pandas-3.0.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:69780c98f286076dcafca38d8b8eee1676adf220199c0a39f0ecbf976b68151a", size = 10864519, upload-time = "2026-01-21T15:50:41.043Z" }, + { url = "https://files.pythonhosted.org/packages/00/8d/50858522cdc46ac88b9afdc3015e298959a70a08cd21e008a44e9520180c/pandas-3.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4a66384f017240f3858a4c8a7cf21b0591c3ac885cddb7758a589f0f71e87ebb", size = 11394124, upload-time = "2026-01-21T15:50:43.377Z" }, + { url = "https://files.pythonhosted.org/packages/86/3f/83b2577db02503cd93d8e95b0f794ad9d4be0ba7cb6c8bcdcac964a34a42/pandas-3.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be8c515c9bc33989d97b89db66ea0cececb0f6e3c2a87fcc8b69443a6923e95f", size = 11920444, upload-time = "2026-01-21T15:50:45.932Z" }, + { url = "https://files.pythonhosted.org/packages/64/2d/4f8a2f192ed12c90a0aab47f5557ece0e56b0370c49de9454a09de7381b2/pandas-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:a453aad8c4f4e9f166436994a33884442ea62aa8b27d007311e87521b97246e1", size = 9730970, upload-time = "2026-01-21T15:50:47.962Z" }, + { url = "https://files.pythonhosted.org/packages/d4/64/ff571be435cf1e643ca98d0945d76732c0b4e9c37191a89c8550b105eed1/pandas-3.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:da768007b5a33057f6d9053563d6b74dd6d029c337d93c6d0d22a763a5c2ecc0", size = 9041950, upload-time = "2026-01-21T15:50:50.422Z" }, + { url = "https://files.pythonhosted.org/packages/6f/fa/7f0ac4ca8877c57537aaff2a842f8760e630d8e824b730eb2e859ffe96ca/pandas-3.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b78d646249b9a2bc191040988c7bb524c92fa8534fb0898a0741d7e6f2ffafa6", size = 10307129, upload-time = "2026-01-21T15:50:52.877Z" }, + { url = "https://files.pythonhosted.org/packages/6f/11/28a221815dcea4c0c9414dfc845e34a84a6a7dabc6da3194498ed5ba4361/pandas-3.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bc9cba7b355cb4162442a88ce495e01cb605f17ac1e27d6596ac963504e0305f", size = 9850201, upload-time = "2026-01-21T15:50:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/ba/da/53bbc8c5363b7e5bd10f9ae59ab250fc7a382ea6ba08e4d06d8694370354/pandas-3.0.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c9a1a149aed3b6c9bf246033ff91e1b02d529546c5d6fb6b74a28fea0cf4c70", size = 10354031, upload-time = "2026-01-21T15:50:57.463Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a3/51e02ebc2a14974170d51e2410dfdab58870ea9bcd37cda15bd553d24dc4/pandas-3.0.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95683af6175d884ee89471842acfca29172a85031fccdabc35e50c0984470a0e", size = 10861165, upload-time = "2026-01-21T15:50:59.32Z" }, + { url = "https://files.pythonhosted.org/packages/a5/fe/05a51e3cac11d161472b8297bd41723ea98013384dd6d76d115ce3482f9b/pandas-3.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1fbbb5a7288719e36b76b4f18d46ede46e7f916b6c8d9915b756b0a6c3f792b3", size = 11359359, upload-time = "2026-01-21T15:51:02.014Z" }, + { url = "https://files.pythonhosted.org/packages/ee/56/ba620583225f9b85a4d3e69c01df3e3870659cc525f67929b60e9f21dcd1/pandas-3.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e8b9808590fa364416b49b2a35c1f4cf2785a6c156935879e57f826df22038e", size = 11912907, upload-time = "2026-01-21T15:51:05.175Z" }, + { url = "https://files.pythonhosted.org/packages/c9/8c/c6638d9f67e45e07656b3826405c5cc5f57f6fd07c8b2572ade328c86e22/pandas-3.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:98212a38a709feb90ae658cb6227ea3657c22ba8157d4b8f913cd4c950de5e7e", size = 9732138, upload-time = "2026-01-21T15:51:07.569Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bf/bd1335c3bf1770b6d8fed2799993b11c4971af93bb1b729b9ebbc02ca2ec/pandas-3.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:177d9df10b3f43b70307a149d7ec49a1229a653f907aa60a48f1877d0e6be3be", size = 9033568, upload-time = "2026-01-21T15:51:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/8e/c6/f5e2171914d5e29b9171d495344097d54e3ffe41d2d85d8115baba4dc483/pandas-3.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2713810ad3806767b89ad3b7b69ba153e1c6ff6d9c20f9c2140379b2a98b6c98", size = 10741936, upload-time = "2026-01-21T15:51:11.693Z" }, + { url = "https://files.pythonhosted.org/packages/51/88/9a0164f99510a1acb9f548691f022c756c2314aad0d8330a24616c14c462/pandas-3.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:15d59f885ee5011daf8335dff47dcb8a912a27b4ad7826dc6cbe809fd145d327", size = 10393884, upload-time = "2026-01-21T15:51:14.197Z" }, + { url = "https://files.pythonhosted.org/packages/e0/53/b34d78084d88d8ae2b848591229da8826d1e65aacf00b3abe34023467648/pandas-3.0.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24e6547fb64d2c92665dd2adbfa4e85fa4fd70a9c070e7cfb03b629a0bbab5eb", size = 10310740, upload-time = "2026-01-21T15:51:16.093Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d3/bee792e7c3d6930b74468d990604325701412e55d7aaf47460a22311d1a5/pandas-3.0.0-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48ee04b90e2505c693d3f8e8f524dab8cb8aaf7ddcab52c92afa535e717c4812", size = 10700014, upload-time = "2026-01-21T15:51:18.818Z" }, + { url = "https://files.pythonhosted.org/packages/55/db/2570bc40fb13aaed1cbc3fbd725c3a60ee162477982123c3adc8971e7ac1/pandas-3.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66f72fb172959af42a459e27a8d8d2c7e311ff4c1f7db6deb3b643dbc382ae08", size = 11323737, upload-time = "2026-01-21T15:51:20.784Z" }, + { url = "https://files.pythonhosted.org/packages/bc/2e/297ac7f21c8181b62a4cccebad0a70caf679adf3ae5e83cb676194c8acc3/pandas-3.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4a4a400ca18230976724a5066f20878af785f36c6756e498e94c2a5e5d57779c", size = 11771558, upload-time = "2026-01-21T15:51:22.977Z" }, + { url = "https://files.pythonhosted.org/packages/0a/46/e1c6876d71c14332be70239acce9ad435975a80541086e5ffba2f249bcf6/pandas-3.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:940eebffe55528074341a5a36515f3e4c5e25e958ebbc764c9502cfc35ba3faa", size = 10473771, upload-time = "2026-01-21T15:51:25.285Z" }, +] + [[package]] name = "pandocfilters" version = "1.5.1" @@ -1467,7 +1538,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess" }, + { name = "ptyprocess", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ @@ -1518,7 +1589,8 @@ dependencies = [ { name = "numexpr" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pandas" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "plotly" }, { name = "psutil" }, { name = "pytest" }, @@ -1535,10 +1607,12 @@ wheels = [ [[package]] name = "policyengine-us" -version = "1.500.3" +version = "1.514.0" source = { editable = "." } dependencies = [ { name = "microdf-python" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "policyengine-core" }, { name = "tqdm" }, ] @@ -1559,6 +1633,7 @@ requires-dist = [ { name = "furo", marker = "extra == 'dev'", specifier = ">=2024.8.6" }, { name = "jupyter-book", marker = "extra == 'dev'", specifier = ">=1.0.4.post1" }, { name = "microdf-python", specifier = ">=1.0.0" }, + { name = "pandas", specifier = ">=2.0" }, { name = "policyengine-core", specifier = ">=3.23.0" }, { name = "setuptools", marker = "extra == 'dev'", specifier = ">=80.9.0" }, { name = "tqdm", specifier = ">=4.67.1" }, @@ -2094,7 +2169,9 @@ name = "sphinx" version = "9.0.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.11.*'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "alabaster", marker = "python_full_version == '3.11.*'" }, @@ -2125,7 +2202,9 @@ name = "sphinx" version = "9.1.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.12'", + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ { name = "alabaster", marker = "python_full_version >= '3.12'" }, From 99a3215822db8e8b175eb6dd0646ab4f1731212d Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 24 Jan 2026 14:56:29 -0500 Subject: [PATCH 4/6] Bump policyengine-core to 3.23.4 for pandas 3 compatibility policyengine-core 3.23.4 includes fixes for: - filled_array handling of pandas StringDtype - VectorialParameterNodeAtInstant handling of StringArray Tests should now pass with pandas 3.0. Co-Authored-By: Claude Opus 4.5 --- changelog_entry.yaml | 1 + pyproject.toml | 2 +- uv.lock | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/changelog_entry.yaml b/changelog_entry.yaml index 639b9a470ff..f7edd791655 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -4,3 +4,4 @@ - Added pandas 3.0 compatibility tests to verify policyengine-core fixes for StringDtype and StringArray handling changed: - Removed pandas <3.0 version cap to enable pandas 3.0 support + - Bumped policyengine-core minimum version to 3.23.4 for pandas 3 compatibility diff --git a/pyproject.toml b/pyproject.toml index bd69d063918..2aeb0125e10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ classifiers = [ dependencies = [ "microdf-python>=1.0.0", "pandas>=2.0", - "policyengine-core>=3.23.0", + "policyengine-core>=3.23.4", "tqdm>=4.67.1", ] diff --git a/uv.lock b/uv.lock index 7fe3a91cc29..f084ae287fa 100644 --- a/uv.lock +++ b/uv.lock @@ -1578,7 +1578,7 @@ wheels = [ [[package]] name = "policyengine-core" -version = "3.23.1" +version = "3.23.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dpath" }, @@ -1600,9 +1600,9 @@ dependencies = [ { name = "standard-imghdr" }, { name = "wheel" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/ff/8a239cca030ad865f815f812725b7d0f2fb9839175c0f0d5e2b392247d6f/policyengine_core-3.23.1.tar.gz", hash = "sha256:a2de3b4398142cb70bdb08b8a64dbb1766f2cbf08cb21c37a83e8650c7d8e075", size = 162809, upload-time = "2025-12-14T23:37:36.289Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/ef/4848f615dd268502a69aa2ae84bb0a8beedb3e2554a33b58f7b9eaa1cfd2/policyengine_core-3.23.4.tar.gz", hash = "sha256:a9e25e7d763c3dd99ea9d3af3bf7cd4163653a75ad0e988e35a8fccbe0b68ce7", size = 163206, upload-time = "2026-01-24T15:42:49.608Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/23/8367becc01e99f1450302eee90a735a4bda10d77f583cb51b98fcad03d51/policyengine_core-3.23.1-py3-none-any.whl", hash = "sha256:49265216e0a90ae727d6f90a70ffc350949aa283e73d60cbed98d5ccbdefbc5e", size = 224715, upload-time = "2025-12-14T23:37:35.151Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3d/b7123bddb3aa26ebedc21ecd65da9eb622dfb4f8b715d922f63fb0c9f410/policyengine_core-3.23.4-py3-none-any.whl", hash = "sha256:799c329800d4d4e6f4020b4f663af9d4a7c6cd2c30572ac2e71601c0894f96df", size = 225061, upload-time = "2026-01-24T15:42:47.776Z" }, ] [[package]] @@ -1634,7 +1634,7 @@ requires-dist = [ { name = "jupyter-book", marker = "extra == 'dev'", specifier = ">=1.0.4.post1" }, { name = "microdf-python", specifier = ">=1.0.0" }, { name = "pandas", specifier = ">=2.0" }, - { name = "policyengine-core", specifier = ">=3.23.0" }, + { name = "policyengine-core", specifier = ">=3.23.4" }, { name = "setuptools", marker = "extra == 'dev'", specifier = ">=80.9.0" }, { name = "tqdm", specifier = ">=4.67.1" }, ] From 7b804c4eeaa24284fea8e007d5806ed00f30d9bf Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 24 Jan 2026 23:23:00 -0500 Subject: [PATCH 5/6] Bump policyengine-core to 3.23.5 for Enum.encode() fix policyengine-core 3.23.5 includes the fix for KeyError when encoding Enum variables with pandas 3.0 StringDtype index. Co-Authored-By: Claude Opus 4.5 --- changelog_entry.yaml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog_entry.yaml b/changelog_entry.yaml index f7edd791655..788fcffaeee 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -4,4 +4,4 @@ - Added pandas 3.0 compatibility tests to verify policyengine-core fixes for StringDtype and StringArray handling changed: - Removed pandas <3.0 version cap to enable pandas 3.0 support - - Bumped policyengine-core minimum version to 3.23.4 for pandas 3 compatibility + - Bumped policyengine-core minimum version to 3.23.5 for pandas 3 compatibility (includes Enum.encode() fix) diff --git a/pyproject.toml b/pyproject.toml index 2aeb0125e10..e295bbe33e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ classifiers = [ dependencies = [ "microdf-python>=1.0.0", "pandas>=2.0", - "policyengine-core>=3.23.4", + "policyengine-core>=3.23.5", "tqdm>=4.67.1", ] From 447180e69f1e8ace5d41eba05add7daeaa9ede5f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 25 Jan 2026 07:36:04 -0500 Subject: [PATCH 6/6] Remove unrelated weeks_unemployed variable This was accidentally included in the pandas 3.0 PR. Removing to get Quick Feedback tests to pass (the new variable was triggering full test suite instead of selective tests). Co-Authored-By: Claude Opus 4.5 --- .../variables/gov/states/weeks_unemployed.py | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 policyengine_us/variables/gov/states/weeks_unemployed.py diff --git a/policyengine_us/variables/gov/states/weeks_unemployed.py b/policyengine_us/variables/gov/states/weeks_unemployed.py deleted file mode 100644 index eb3de6504eb..00000000000 --- a/policyengine_us/variables/gov/states/weeks_unemployed.py +++ /dev/null @@ -1,13 +0,0 @@ -from policyengine_us.model_api import * - - -class weeks_unemployed(Variable): - value_type = float - entity = Person - label = "weeks unemployed" - unit = "week" - documentation = ( - "Number of weeks during the year the person was looking for work. " - "From CPS ASEC variable LKWEEKS." - ) - definition_period = YEAR