From 0231af43ab37b7edd900537e6719131b0483349c Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 20 Nov 2024 13:39:46 -0600 Subject: [PATCH 01/59] Add basic Python project with just vars_rename --- .pre-commit-config.yaml | 12 ++ python/.gitignore | 36 ++++ python/.python-version | 1 + python/README.md | 16 ++ python/ccao/__init__.py | 6 + python/ccao/data/__init__.py | 0 python/ccao/data/vars_dict.csv | 1 + python/ccao/data/vars_dict_legacy.csv | 1 + python/ccao/vars_funs.py | 119 +++++++++++ python/pyproject.toml | 20 ++ python/tests/__init__.py | 0 python/uv.lock | 281 ++++++++++++++++++++++++++ 12 files changed, 493 insertions(+) create mode 100644 python/.gitignore create mode 100644 python/.python-version create mode 100644 python/README.md create mode 100644 python/ccao/__init__.py create mode 100644 python/ccao/data/__init__.py create mode 120000 python/ccao/data/vars_dict.csv create mode 120000 python/ccao/data/vars_dict_legacy.csv create mode 100644 python/ccao/vars_funs.py create mode 100644 python/pyproject.toml create mode 100644 python/tests/__init__.py create mode 100644 python/uv.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 11a5038..c1e3684 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,3 +33,15 @@ repos: entry: Cannot commit .Rhistory, .RData, .Rds or .rds. language: fail files: '\.(Rhistory|RData|Rds|rds)$' +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.7.4 + hooks: + # Python linter. Ruff recommends running this before the formatter to + # avoid conflicts when using the --fix flag + - id: ruff + args: + - --fix + files: ^python/ + # Formatter + - id: ruff-format + files: ^python/ diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 0000000..f0b0438 --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,36 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ diff --git a/python/.python-version b/python/.python-version new file mode 100644 index 0000000..2c07333 --- /dev/null +++ b/python/.python-version @@ -0,0 +1 @@ +3.11 diff --git a/python/README.md b/python/README.md new file mode 100644 index 0000000..ca9680b --- /dev/null +++ b/python/README.md @@ -0,0 +1,16 @@ +# CCAO Python package + +This is a Python version of the [`ccao` R +package](https://ccao-data.github.io/ccao/), providing utilities for +managing, distributing, and version controlling *CCAO-specific* functions +used throughout CCAO applications, models, and diagnostics. For generalized +versions of assessment-related functions, see +[assesspy](https://github.com/ccao-data/assesspy) + +## Installation + +You can install the released version of `ccao` directly from GitHub: + +```bash +pip install "git+https://github.com/ccao-data/ccao.git#egg=ccao&subdirectory=python" +``` diff --git a/python/ccao/__init__.py b/python/ccao/__init__.py new file mode 100644 index 0000000..52eac0d --- /dev/null +++ b/python/ccao/__init__.py @@ -0,0 +1,6 @@ +# Use explicit re-exports to appease flake8 +from ccao.vars_funs import ( + vars_dict as vars_dict, + vars_dict_legacy as vars_dict_legacy, + vars_rename as vars_rename, +) diff --git a/python/ccao/data/__init__.py b/python/ccao/data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/ccao/data/vars_dict.csv b/python/ccao/data/vars_dict.csv new file mode 120000 index 0000000..f61f4c0 --- /dev/null +++ b/python/ccao/data/vars_dict.csv @@ -0,0 +1 @@ +../../../data-raw/vars_dict.csv \ No newline at end of file diff --git a/python/ccao/data/vars_dict_legacy.csv b/python/ccao/data/vars_dict_legacy.csv new file mode 120000 index 0000000..0f46a75 --- /dev/null +++ b/python/ccao/data/vars_dict_legacy.csv @@ -0,0 +1 @@ +../../../data-raw/vars_dict_legacy.csv \ No newline at end of file diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py new file mode 100644 index 0000000..939954d --- /dev/null +++ b/python/ccao/vars_funs.py @@ -0,0 +1,119 @@ +import enum +import importlib.resources + +import pandas as pd + +import ccao.data + +# Load the default variable dictionary +_data_path = importlib.resources.files(ccao.data) +vars_dict = pd.read_csv(str(_data_path / "vars_dict.csv")) +vars_dict_legacy = pd.read_csv(str(_data_path / "vars_dict_legacy.csv")) + + +class Name(enum.Enum): + """Valid sources of names for variables.""" + + HIE = "hie" + IASWORLD = "iasworld" + ATHENA = "athena" + MODEL = "model" + PUBLISH = "publish" + PRETTY = "pretty" + + @classmethod + def values(cls) -> list[str]: + """Return all possible values for the enum.""" + return [name.value for name in cls] + + @property + def colname(self) -> str: + """Get the dictionary column corresponding to this variable name.""" + return f"var_name_{self.value}" + + +def vars_rename( + data: list[str] | pd.DataFrame, + names_from: Name | str, + names_to: Name | str, + inplace: bool = True, + dictionary: pd.DataFrame | None = None, +) -> list[str] | pd.DataFrame: + """ + Rename variables from one naming convention to another. + + This function renames columns in a dataset based on a dictionary that maps + names from one convention to another. It can rename columns in-place or return + a character vector of renamed columns. + + :param data: DataFrame or list of column names. + The input data with columns to be renamed. If a DataFrame, renames columns directly. + :type data: pandas.DataFrame or list[str] + + :param names_from: The source naming convention to rename from. + Must match a key in the dictionary. + :type names_from: Name enum or str + + :param names_to: The target naming convention to rename to. + Must match a key in the dictionary. + :type names_to: Name enum or str + + :param inplace: Whether to mutate the data in place or return the new names as a list of strings. + Only used if the input data is a DataFrame. + :type inplace: str + + :param dictionary: The dictionary for mapping column names. + Must contain keys like `var_name_` and `var_name_`. + :type dictionary: pandas.DataFrame + + :raises ValueError: If required arguments are invalid or the dictionary does not meet format requirements. + :raises TypeError: If `data` is neither a DataFrame nor a list of column names. + + :return: Either the input data with renamed columns if `inplace is True` + and the input data is a DataFrame, otherwise a list of renamed columns. + :rtype: pandas.DataFrame or list[str] + + :example: + >>> vars_rename(data=sample_data, names_from=Name.MODEL, names_to=Name.ATHENA, inplace=False) + """ + # Validate the dictionary schema + dictionary = dictionary or vars_dict + if not isinstance(dictionary, pd.DataFrame) or len(dictionary) == 0: + raise ValueError("dictionary must be a non-empty pandas DataFrame") + + if not (isinstance(names_from, Name) or isinstance(names_from, str)) or not ( + isinstance(names_to, Name) or isinstance(names_to, str) + ): + raise ValueError("names_from and names_to must be strings or Name instances") + + if isinstance(names_from, str): + if names_from.lower() not in Name.values(): + raise ValueError(f"names_from must be one of: {Name.values()}") + names_from = Name[names_from.upper()] + + if isinstance(names_to, str): + if names_to.lower() not in Name.values(): + raise ValueError(f"names_to must be one of: {Name.values()}") + names_to = Name[names_to.upper()] + + dictionary_columns = list(dictionary.columns.values) + if ( + names_from.colname not in dictionary_columns + or names_to.colname not in dictionary_columns + ): + raise ValueError( + f"{names_from.colname} and {names_to.colname} must be columns in dictionary" + ) + + mapping = dict(zip(dictionary[names_from.colname], dictionary[names_to.colname])) + + if isinstance(data, pd.DataFrame): + if inplace is True: + data.rename(columns=mapping, inplace=True) + return data + else: + return [mapping.get(col, col) for col in list(data.columns.values)] + elif isinstance(data, list): + return [mapping.get(col, col) for col in data] + else: + raise TypeError("data must be a DataFrame or list of column names") diff --git a/python/pyproject.toml b/python/pyproject.toml new file mode 100644 index 0000000..322e256 --- /dev/null +++ b/python/pyproject.toml @@ -0,0 +1,20 @@ +[project] +name = "ccao" +version = "1.3.0" +description = "Convenience Functions and Datasets for the Cook County Assessor's Office" +readme = "README.md" +requires-python = ">=3.11" +authors = [ + {name = "Dan Snow", email="daniel.snow@cookcountyil.gov"}, + {name = "Jean Cochrane", email="jean.cochrane@cookcountyil.gov"}, +] +dependencies = [ + "pandas>=2.2.3", +] + +[dependency-groups] +dev = [ + "mypy>=1.13.0", + "pytest>=8.3.3", + "ruff>=0.7.4", +] diff --git a/python/tests/__init__.py b/python/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/uv.lock b/python/uv.lock new file mode 100644 index 0000000..eb2f398 --- /dev/null +++ b/python/uv.lock @@ -0,0 +1,281 @@ +version = 1 +requires-python = ">=3.11" +resolution-markers = [ + "python_full_version < '3.12'", + "python_full_version >= '3.12'", +] + +[[package]] +name = "ccao" +version = "1.3.0" +source = { virtual = "." } +dependencies = [ + { name = "pandas" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [{ name = "pandas", specifier = ">=2.2.3" }] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy", specifier = ">=1.13.0" }, + { name = "pytest", specifier = ">=8.3.3" }, + { name = "ruff", specifier = ">=0.7.4" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "mypy" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/21/7e9e523537991d145ab8a0a2fd98548d67646dc2aaaf6091c31ad883e7c1/mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", size = 3152532 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/19/de0822609e5b93d02579075248c7aa6ceaddcea92f00bf4ea8e4c22e3598/mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d", size = 10939027 }, + { url = "https://files.pythonhosted.org/packages/c8/71/6950fcc6ca84179137e4cbf7cf41e6b68b4a339a1f5d3e954f8c34e02d66/mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d", size = 10108699 }, + { url = "https://files.pythonhosted.org/packages/26/50/29d3e7dd166e74dc13d46050b23f7d6d7533acf48f5217663a3719db024e/mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b", size = 12506263 }, + { url = "https://files.pythonhosted.org/packages/3f/1d/676e76f07f7d5ddcd4227af3938a9c9640f293b7d8a44dd4ff41d4db25c1/mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73", size = 12984688 }, + { url = "https://files.pythonhosted.org/packages/9c/03/5a85a30ae5407b1d28fab51bd3e2103e52ad0918d1e68f02a7778669a307/mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca", size = 9626811 }, + { url = "https://files.pythonhosted.org/packages/fb/31/c526a7bd2e5c710ae47717c7a5f53f616db6d9097caf48ad650581e81748/mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5", size = 11077900 }, + { url = "https://files.pythonhosted.org/packages/83/67/b7419c6b503679d10bd26fc67529bc6a1f7a5f220bbb9f292dc10d33352f/mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e", size = 10074818 }, + { url = "https://files.pythonhosted.org/packages/ba/07/37d67048786ae84e6612575e173d713c9a05d0ae495dde1e68d972207d98/mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2", size = 12589275 }, + { url = "https://files.pythonhosted.org/packages/1f/17/b1018c6bb3e9f1ce3956722b3bf91bff86c1cefccca71cec05eae49d6d41/mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0", size = 13037783 }, + { url = "https://files.pythonhosted.org/packages/cb/32/cd540755579e54a88099aee0287086d996f5a24281a673f78a0e14dba150/mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2", size = 9726197 }, + { url = "https://files.pythonhosted.org/packages/11/bb/ab4cfdc562cad80418f077d8be9b4491ee4fb257440da951b85cbb0a639e/mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", size = 11069721 }, + { url = "https://files.pythonhosted.org/packages/59/3b/a393b1607cb749ea2c621def5ba8c58308ff05e30d9dbdc7c15028bca111/mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", size = 10063996 }, + { url = "https://files.pythonhosted.org/packages/d1/1f/6b76be289a5a521bb1caedc1f08e76ff17ab59061007f201a8a18cc514d1/mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8", size = 12584043 }, + { url = "https://files.pythonhosted.org/packages/a6/83/5a85c9a5976c6f96e3a5a7591aa28b4a6ca3a07e9e5ba0cec090c8b596d6/mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", size = 13036996 }, + { url = "https://files.pythonhosted.org/packages/b4/59/c39a6f752f1f893fccbcf1bdd2aca67c79c842402b5283563d006a67cf76/mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", size = 9737709 }, + { url = "https://files.pythonhosted.org/packages/3b/86/72ce7f57431d87a7ff17d442f521146a6585019eb8f4f31b7c02801f78ad/mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", size = 2647043 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "numpy" +version = "2.1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ca/1166b75c21abd1da445b97bf1fa2f14f423c6cfb4fc7c4ef31dccf9f6a94/numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761", size = 20166090 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/81/c8167192eba5247593cd9d305ac236847c2912ff39e11402e72ae28a4985/numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d", size = 21156252 }, + { url = "https://files.pythonhosted.org/packages/da/74/5a60003fc3d8a718d830b08b654d0eea2d2db0806bab8f3c2aca7e18e010/numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41", size = 13784119 }, + { url = "https://files.pythonhosted.org/packages/47/7c/864cb966b96fce5e63fcf25e1e4d957fe5725a635e5f11fe03f39dd9d6b5/numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9", size = 5352978 }, + { url = "https://files.pythonhosted.org/packages/09/ac/61d07930a4993dd9691a6432de16d93bbe6aa4b1c12a5e573d468eefc1ca/numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09", size = 6892570 }, + { url = "https://files.pythonhosted.org/packages/27/2f/21b94664f23af2bb52030653697c685022119e0dc93d6097c3cb45bce5f9/numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a", size = 13896715 }, + { url = "https://files.pythonhosted.org/packages/7a/f0/80811e836484262b236c684a75dfc4ba0424bc670e765afaa911468d9f39/numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b", size = 16339644 }, + { url = "https://files.pythonhosted.org/packages/fa/81/ce213159a1ed8eb7d88a2a6ef4fbdb9e4ffd0c76b866c350eb4e3c37e640/numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee", size = 16712217 }, + { url = "https://files.pythonhosted.org/packages/7d/84/4de0b87d5a72f45556b2a8ee9fc8801e8518ec867fc68260c1f5dcb3903f/numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0", size = 14399053 }, + { url = "https://files.pythonhosted.org/packages/7e/1c/e5fabb9ad849f9d798b44458fd12a318d27592d4bc1448e269dec070ff04/numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9", size = 6534741 }, + { url = "https://files.pythonhosted.org/packages/1e/48/a9a4b538e28f854bfb62e1dea3c8fea12e90216a276c7777ae5345ff29a7/numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2", size = 12869487 }, + { url = "https://files.pythonhosted.org/packages/8a/f0/385eb9970309643cbca4fc6eebc8bb16e560de129c91258dfaa18498da8b/numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e", size = 20849658 }, + { url = "https://files.pythonhosted.org/packages/54/4a/765b4607f0fecbb239638d610d04ec0a0ded9b4951c56dc68cef79026abf/numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958", size = 13492258 }, + { url = "https://files.pythonhosted.org/packages/bd/a7/2332679479c70b68dccbf4a8eb9c9b5ee383164b161bee9284ac141fbd33/numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8", size = 5090249 }, + { url = "https://files.pythonhosted.org/packages/c1/67/4aa00316b3b981a822c7a239d3a8135be2a6945d1fd11d0efb25d361711a/numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564", size = 6621704 }, + { url = "https://files.pythonhosted.org/packages/5e/da/1a429ae58b3b6c364eeec93bf044c532f2ff7b48a52e41050896cf15d5b1/numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512", size = 13606089 }, + { url = "https://files.pythonhosted.org/packages/9e/3e/3757f304c704f2f0294a6b8340fcf2be244038be07da4cccf390fa678a9f/numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b", size = 16043185 }, + { url = "https://files.pythonhosted.org/packages/43/97/75329c28fea3113d00c8d2daf9bc5828d58d78ed661d8e05e234f86f0f6d/numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc", size = 16410751 }, + { url = "https://files.pythonhosted.org/packages/ad/7a/442965e98b34e0ae9da319f075b387bcb9a1e0658276cc63adb8c9686f7b/numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0", size = 14082705 }, + { url = "https://files.pythonhosted.org/packages/ac/b6/26108cf2cfa5c7e03fb969b595c93131eab4a399762b51ce9ebec2332e80/numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9", size = 6239077 }, + { url = "https://files.pythonhosted.org/packages/a6/84/fa11dad3404b7634aaab50733581ce11e5350383311ea7a7010f464c0170/numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a", size = 12566858 }, + { url = "https://files.pythonhosted.org/packages/4d/0b/620591441457e25f3404c8057eb924d04f161244cb8a3680d529419aa86e/numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f", size = 20836263 }, + { url = "https://files.pythonhosted.org/packages/45/e1/210b2d8b31ce9119145433e6ea78046e30771de3fe353f313b2778142f34/numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598", size = 13507771 }, + { url = "https://files.pythonhosted.org/packages/55/44/aa9ee3caee02fa5a45f2c3b95cafe59c44e4b278fbbf895a93e88b308555/numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57", size = 5075805 }, + { url = "https://files.pythonhosted.org/packages/78/d6/61de6e7e31915ba4d87bbe1ae859e83e6582ea14c6add07c8f7eefd8488f/numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe", size = 6608380 }, + { url = "https://files.pythonhosted.org/packages/3e/46/48bdf9b7241e317e6cf94276fe11ba673c06d1fdf115d8b4ebf616affd1a/numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43", size = 13602451 }, + { url = "https://files.pythonhosted.org/packages/70/50/73f9a5aa0810cdccda9c1d20be3cbe4a4d6ea6bfd6931464a44c95eef731/numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56", size = 16039822 }, + { url = "https://files.pythonhosted.org/packages/ad/cd/098bc1d5a5bc5307cfc65ee9369d0ca658ed88fbd7307b0d49fab6ca5fa5/numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a", size = 16411822 }, + { url = "https://files.pythonhosted.org/packages/83/a2/7d4467a2a6d984549053b37945620209e702cf96a8bc658bc04bba13c9e2/numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef", size = 14079598 }, + { url = "https://files.pythonhosted.org/packages/e9/6a/d64514dcecb2ee70bfdfad10c42b76cab657e7ee31944ff7a600f141d9e9/numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f", size = 6236021 }, + { url = "https://files.pythonhosted.org/packages/bb/f9/12297ed8d8301a401e7d8eb6b418d32547f1d700ed3c038d325a605421a4/numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed", size = 12560405 }, + { url = "https://files.pythonhosted.org/packages/a7/45/7f9244cd792e163b334e3a7f02dff1239d2890b6f37ebf9e82cbe17debc0/numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f", size = 20859062 }, + { url = "https://files.pythonhosted.org/packages/b1/b4/a084218e7e92b506d634105b13e27a3a6645312b93e1c699cc9025adb0e1/numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4", size = 13515839 }, + { url = "https://files.pythonhosted.org/packages/27/45/58ed3f88028dcf80e6ea580311dc3edefdd94248f5770deb980500ef85dd/numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e", size = 5116031 }, + { url = "https://files.pythonhosted.org/packages/37/a8/eb689432eb977d83229094b58b0f53249d2209742f7de529c49d61a124a0/numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0", size = 6629977 }, + { url = "https://files.pythonhosted.org/packages/42/a3/5355ad51ac73c23334c7caaed01adadfda49544f646fcbfbb4331deb267b/numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408", size = 13575951 }, + { url = "https://files.pythonhosted.org/packages/c4/70/ea9646d203104e647988cb7d7279f135257a6b7e3354ea6c56f8bafdb095/numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6", size = 16022655 }, + { url = "https://files.pythonhosted.org/packages/14/ce/7fc0612903e91ff9d0b3f2eda4e18ef9904814afcae5b0f08edb7f637883/numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f", size = 16399902 }, + { url = "https://files.pythonhosted.org/packages/ef/62/1d3204313357591c913c32132a28f09a26357e33ea3c4e2fe81269e0dca1/numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17", size = 14067180 }, + { url = "https://files.pythonhosted.org/packages/24/d7/78a40ed1d80e23a774cb8a34ae8a9493ba1b4271dde96e56ccdbab1620ef/numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48", size = 6291907 }, + { url = "https://files.pythonhosted.org/packages/86/09/a5ab407bd7f5f5599e6a9261f964ace03a73e7c6928de906981c31c38082/numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4", size = 12644098 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pandas" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/44/d9502bf0ed197ba9bf1103c9867d5904ddcaf869e52329787fc54ed70cc8/pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", size = 12602222 }, + { url = "https://files.pythonhosted.org/packages/52/11/9eac327a38834f162b8250aab32a6781339c69afe7574368fffe46387edf/pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", size = 11321274 }, + { url = "https://files.pythonhosted.org/packages/45/fb/c4beeb084718598ba19aa9f5abbc8aed8b42f90930da861fcb1acdb54c3a/pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", size = 15579836 }, + { url = "https://files.pythonhosted.org/packages/cd/5f/4dba1d39bb9c38d574a9a22548c540177f78ea47b32f99c0ff2ec499fac5/pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc", size = 13058505 }, + { url = "https://files.pythonhosted.org/packages/b9/57/708135b90391995361636634df1f1130d03ba456e95bcf576fada459115a/pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3", size = 16744420 }, + { url = "https://files.pythonhosted.org/packages/86/4a/03ed6b7ee323cf30404265c284cee9c65c56a212e0a08d9ee06984ba2240/pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32", size = 14440457 }, + { url = "https://files.pythonhosted.org/packages/ed/8c/87ddf1fcb55d11f9f847e3c69bb1c6f8e46e2f40ab1a2d2abadb2401b007/pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5", size = 11617166 }, + { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893 }, + { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475 }, + { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645 }, + { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445 }, + { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235 }, + { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756 }, + { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 }, + { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 }, + { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 }, + { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 }, + { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 }, + { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 }, + { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 }, + { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 }, + { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 }, + { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 }, + { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 }, + { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 }, + { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 }, + { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pytest" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pytz" +version = "2024.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, +] + +[[package]] +name = "ruff" +version = "0.7.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/8b/bc4e0dfa1245b07cf14300e10319b98e958a53ff074c1dd86b35253a8c2a/ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2", size = 3275547 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/4b/f5094719e254829766b807dadb766841124daba75a37da83e292ae5ad12f/ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478", size = 10447512 }, + { url = "https://files.pythonhosted.org/packages/9e/1d/3d2d2c9f601cf6044799c5349ff5267467224cefed9b35edf5f1f36486e9/ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63", size = 10235436 }, + { url = "https://files.pythonhosted.org/packages/62/83/42a6ec6216ded30b354b13e0e9327ef75a3c147751aaf10443756cb690e9/ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20", size = 9888936 }, + { url = "https://files.pythonhosted.org/packages/4d/26/e1e54893b13046a6ad05ee9b89ee6f71542ba250f72b4c7a7d17c3dbf73d/ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109", size = 10697353 }, + { url = "https://files.pythonhosted.org/packages/21/24/98d2e109c4efc02bfef144ec6ea2c3e1217e7ce0cfddda8361d268dfd499/ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452", size = 10228078 }, + { url = "https://files.pythonhosted.org/packages/ad/b7/964c75be9bc2945fc3172241b371197bb6d948cc69e28bc4518448c368f3/ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea", size = 11264823 }, + { url = "https://files.pythonhosted.org/packages/12/8d/20abdbf705969914ce40988fe71a554a918deaab62c38ec07483e77866f6/ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7", size = 11951855 }, + { url = "https://files.pythonhosted.org/packages/b8/fc/6519ce58c57b4edafcdf40920b7273dfbba64fc6ebcaae7b88e4dc1bf0a8/ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05", size = 11516580 }, + { url = "https://files.pythonhosted.org/packages/37/1a/5ec1844e993e376a86eb2456496831ed91b4398c434d8244f89094758940/ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06", size = 12692057 }, + { url = "https://files.pythonhosted.org/packages/50/90/76867152b0d3c05df29a74bb028413e90f704f0f6701c4801174ba47f959/ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc", size = 11085137 }, + { url = "https://files.pythonhosted.org/packages/c8/eb/0a7cb6059ac3555243bd026bb21785bbc812f7bbfa95a36c101bd72b47ae/ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172", size = 10681243 }, + { url = "https://files.pythonhosted.org/packages/5e/76/2270719dbee0fd35780b05c08a07b7a726c3da9f67d9ae89ef21fc18e2e5/ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a", size = 10319187 }, + { url = "https://files.pythonhosted.org/packages/9f/e5/39100f72f8ba70bec1bd329efc880dea8b6c1765ea1cb9d0c1c5f18b8d7f/ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd", size = 10803715 }, + { url = "https://files.pythonhosted.org/packages/a5/89/40e904784f305fb56850063f70a998a64ebba68796d823dde67e89a24691/ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a", size = 11162912 }, + { url = "https://files.pythonhosted.org/packages/8d/1b/dd77503b3875c51e3dbc053fd8367b845ab8b01c9ca6d0c237082732856c/ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac", size = 8702767 }, + { url = "https://files.pythonhosted.org/packages/63/76/253ddc3e89e70165bba952ecca424b980b8d3c2598ceb4fc47904f424953/ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6", size = 9497534 }, + { url = "https://files.pythonhosted.org/packages/aa/70/f8724f31abc0b329ca98b33d73c14020168babcf71b0cba3cded5d9d0e66/ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f", size = 8851590 }, +] + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "tzdata" +version = "2024.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/34/943888654477a574a86a98e9896bae89c7aa15078ec29f490fef2f1e5384/tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", size = 193282 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586 }, +] From a738b8bdfa6a938c4a57f7fb6ab9b54361c7120c Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 20 Nov 2024 16:15:29 -0600 Subject: [PATCH 02/59] Add unit tests for vars_rename --- python/ccao/__init__.py | 1 - python/ccao/data/vars_dict_legacy.csv | 1 - python/ccao/vars_funs.py | 121 ++++++------ python/tests/conftest.py | 18 ++ python/tests/fixtures/chars_sample_athena.csv | 71 ++++++++ python/tests/fixtures/chars_sample_hie.csv | 12 ++ python/tests/test_vars_funs.py | 172 ++++++++++++++++++ 7 files changed, 342 insertions(+), 54 deletions(-) delete mode 120000 python/ccao/data/vars_dict_legacy.csv create mode 100644 python/tests/conftest.py create mode 100755 python/tests/fixtures/chars_sample_athena.csv create mode 100755 python/tests/fixtures/chars_sample_hie.csv create mode 100644 python/tests/test_vars_funs.py diff --git a/python/ccao/__init__.py b/python/ccao/__init__.py index 52eac0d..62100ed 100644 --- a/python/ccao/__init__.py +++ b/python/ccao/__init__.py @@ -1,6 +1,5 @@ # Use explicit re-exports to appease flake8 from ccao.vars_funs import ( vars_dict as vars_dict, - vars_dict_legacy as vars_dict_legacy, vars_rename as vars_rename, ) diff --git a/python/ccao/data/vars_dict_legacy.csv b/python/ccao/data/vars_dict_legacy.csv deleted file mode 120000 index 0f46a75..0000000 --- a/python/ccao/data/vars_dict_legacy.csv +++ /dev/null @@ -1 +0,0 @@ -../../../data-raw/vars_dict_legacy.csv \ No newline at end of file diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index 939954d..dcb6b19 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -8,35 +8,28 @@ # Load the default variable dictionary _data_path = importlib.resources.files(ccao.data) vars_dict = pd.read_csv(str(_data_path / "vars_dict.csv")) -vars_dict_legacy = pd.read_csv(str(_data_path / "vars_dict_legacy.csv")) +# Prefix we use to identify variable name columns in the variable dictionary +var_name_prefix = "var_name" -class Name(enum.Enum): - """Valid sources of names for variables.""" - HIE = "hie" - IASWORLD = "iasworld" - ATHENA = "athena" - MODEL = "model" - PUBLISH = "publish" - PRETTY = "pretty" +class OutputType(enum.Enum): + """Possible output types for variable renaming""" + + INPLACE = "inplace" + VECTOR = "vector" @classmethod def values(cls) -> list[str]: - """Return all possible values for the enum.""" - return [name.value for name in cls] - - @property - def colname(self) -> str: - """Get the dictionary column corresponding to this variable name.""" - return f"var_name_{self.value}" + """Get the possible values for this enum""" + return [type_.value for type_ in cls] def vars_rename( data: list[str] | pd.DataFrame, - names_from: Name | str, - names_to: Name | str, - inplace: bool = True, + names_from: str, + names_to: str, + output_type: OutputType | str = OutputType.INPLACE, dictionary: pd.DataFrame | None = None, ) -> list[str] | pd.DataFrame: """ @@ -52,15 +45,16 @@ def vars_rename( :param names_from: The source naming convention to rename from. Must match a key in the dictionary. - :type names_from: Name enum or str + :type names_from: str :param names_to: The target naming convention to rename to. Must match a key in the dictionary. - :type names_to: Name enum or str + :type names_to: str - :param inplace: Whether to mutate the data in place or return the new names as a list of strings. - Only used if the input data is a DataFrame. - :type inplace: str + :param output_type: Output type. Either `"inplace"`, which renames the + input data frame, or `"vector"`, which returns a list of strings with + the construction new_col_name = old_col_name. + :type output_type: OutputType or str :param dictionary: The dictionary for mapping column names. Must contain keys like `var_name_` and `var_name_`. @@ -69,51 +63,74 @@ def vars_rename( :raises ValueError: If required arguments are invalid or the dictionary does not meet format requirements. :raises TypeError: If `data` is neither a DataFrame nor a list of column names. - :return: Either the input data with renamed columns if `inplace is True` - and the input data is a DataFrame, otherwise a list of renamed columns. + :return: Either the input data with renamed columns if `output_type` is + `"inplace"` and the input data is a DataFrame, otherwise a list of + renamed columns. :rtype: pandas.DataFrame or list[str] :example: - >>> vars_rename(data=sample_data, names_from=Name.MODEL, names_to=Name.ATHENA, inplace=False) + >>> vars_rename(data=sample_data, names_from="model", names_to="athena", output_type="inplace") """ # Validate the dictionary schema - dictionary = dictionary or vars_dict + dictionary = dictionary if dictionary is not None else vars_dict if not isinstance(dictionary, pd.DataFrame) or len(dictionary) == 0: raise ValueError("dictionary must be a non-empty pandas DataFrame") - if not (isinstance(names_from, Name) or isinstance(names_from, str)) or not ( - isinstance(names_to, Name) or isinstance(names_to, str) - ): - raise ValueError("names_from and names_to must be strings or Name instances") - - if isinstance(names_from, str): - if names_from.lower() not in Name.values(): - raise ValueError(f"names_from must be one of: {Name.values()}") - names_from = Name[names_from.upper()] - - if isinstance(names_to, str): - if names_to.lower() not in Name.values(): - raise ValueError(f"names_to must be one of: {Name.values()}") - names_to = Name[names_to.upper()] - - dictionary_columns = list(dictionary.columns.values) - if ( - names_from.colname not in dictionary_columns - or names_to.colname not in dictionary_columns - ): + # Make sure the dictionary contains variable columns + dictionary_var_columns = [ + col + for col in list(dictionary.columns.values) + if col.startswith(var_name_prefix) + ] + if not len(dictionary_var_columns) >= 2: raise ValueError( - f"{names_from.colname} and {names_to.colname} must be columns in dictionary" + f"dictionary must contain at least two columns starting with " + f"{var_name_prefix}" ) - mapping = dict(zip(dictionary[names_from.colname], dictionary[names_to.colname])) - + # Get a list of possible names_from and names_to from dictionary + possible_names_args = [ + col.replace(f"{var_name_prefix}_", "") for col in dictionary_var_columns + ] + + # Validate names arguments + if not isinstance(names_from, str) or not isinstance(names_to, str): + raise ValueError("names_from and names_to must be strings") + + # If names arguments aren't possible, throw error and list possible names + for label, var in [("names_from", names_from), ("names_to", names_to)]: + if var not in possible_names_args: + raise ValueError( + f"{label} must be one of {possible_names_args} (got '{var}')" + ) + + # Validate output type and convert it to the enum + if not isinstance(output_type, (OutputType, str)): + raise ValueError("output_type must be a string or OutputType instance") + + if isinstance(output_type, str): + if output_type not in OutputType.values(): + raise ValueError( + f"output_type must be one of {OutputType.values()} " + f"(got {output_type})" + ) + output_type = OutputType(output_type) + + # Get a mapping from names_from to names_to + from_ = f"{var_name_prefix}_{names_from}" + to = f"{var_name_prefix}_{names_to}" + mapping = dict(zip(dictionary[from_], dictionary[to])) + + # Handle output differently depending on the input type and `inplace` kwarg if isinstance(data, pd.DataFrame): - if inplace is True: + if output_type == OutputType.INPLACE: data.rename(columns=mapping, inplace=True) return data else: return [mapping.get(col, col) for col in list(data.columns.values)] elif isinstance(data, list): + # If the input data is a list, it's not possible to update it inplace, + # so ignore that argument return [mapping.get(col, col) for col in data] else: raise TypeError("data must be a DataFrame or list of column names") diff --git a/python/tests/conftest.py b/python/tests/conftest.py new file mode 100644 index 0000000..2d59d8b --- /dev/null +++ b/python/tests/conftest.py @@ -0,0 +1,18 @@ +import pathlib + +import pandas as pd +import pytest + +fixture_dir = pathlib.Path(__file__).parent / "fixtures" + + +@pytest.fixture(scope="module") +def chars_sample_athena() -> pd.DataFrame: + """Sample chars with Athena variable names""" + return pd.read_csv(fixture_dir / "chars_sample_athena.csv") + + +@pytest.fixture(scope="module") +def chars_sample_hie() -> pd.DataFrame: + """Sample chars with HIE variable names""" + return pd.read_csv(fixture_dir / "chars_sample_hie.csv") diff --git a/python/tests/fixtures/chars_sample_athena.csv b/python/tests/fixtures/chars_sample_athena.csv new file mode 100755 index 0000000..3fb5c3a --- /dev/null +++ b/python/tests/fixtures/chars_sample_athena.csv @@ -0,0 +1,71 @@ +"pin","year","class","char_yrblt","char_bldg_sf","char_land_sf","char_beds","char_rooms","char_fbath","char_hbath","char_frpl","char_type_resd","char_cnst_qlty","char_apts","char_tp_dsgn","char_attic_fnsh","char_gar1_att","char_gar1_area","char_gar1_size","char_gar1_cnst","char_attic_type","char_bsmt","char_ext_wall","char_heat","char_repair_cnd","char_bsmt_fin","char_roof_cnst","char_use","char_age","char_site","char_ncu","char_renovation","char_porch","char_air","char_tp_plan" +"10254170360000","2015","205",1948,1775,4340,4,7,2,1,2,2,"2","6","2","3","2","2","7","0","3","1","2","1","2","1","1","1","67","2","0","0","0","1","2" +"09363230550000","2019","203",1923,1200,4375,3,5,1,1,0,1,"2","6","2","3","2","2","7","0","3","1","3","2","2","3","1","1","95","2","0","0","1","2","2" +"09363230550000","2016","203",1923,1200,4375,3,5,1,1,0,1,"2","6","2","3","2","2","7","0","3","1","3","2","2","3","1","1","92","2","0","0","1","2","2" +"14321260280000","2018","211",1878,2850,3125,4,9,3,0,0,3,"2","2","2","3","2","2","7","0","3","3","2","1","2","1","2","2","140","2","0","0","0","2","2" +"10253190450000","2018","204",1951,2469,11160,3,8,2,1,1,1,"2","6","0","3","1","2","3","2","3","3","2","1","2","3","1","1","67","2","0","0","0","1","2" +"14321260280000","2016","211",1878,2850,3125,4,9,3,0,0,3,"2","2","2","3","2","2","7","0","3","3","2","1","2","1","2","2","137","2","0","0","0","2","2" +"09254040180000","2020","203",1955,1571,3750,3,6,1,0,0,5,"2","6","2","1","2","2","7","0","1","1","2","1","2","3","1","1","63","2","0","0","0","1","2" +"13362270230000","2015","202",1890,756,2750,2,4,1,1,0,1,"2","6","0","3","2","2","3","1","1","1","3","2","3","1","1","1","125","2","0","0","1","2","2" +"13253160160000","2020","202",1904,706,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","1","1","2","2","2","3","1","1","117","2","0","0","0","2","2" +"10254170360000","2018","205",1948,1829,4340,3,7,2,1,2,2,"2","6","2","3","2","2","3","1","3","1","2","1","2","1","1","1","70","2","0","0","0","1","2" +"13253160160000","2017","202",1901,706,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","1","1","2","2","2","3","1","1","114","2","0","0","0","2","2" +"09363230550000","2017","203",1923,1200,4375,3,5,1,1,0,1,"2","6","2","3","2","2","7","0","3","1","3","2","2","3","1","1","92","2","0","0","1","2","2" +"10253190450000","2017","204",1951,2469,11160,3,8,2,1,1,1,"2","6","0","3","1","2","3","2","3","3","2","1","2","3","1","1","64","2","0","0","0","1","2" +"13253230040000","2019","203",1969,1040,3150,2,5,1,0,0,1,"2","6","2","3","2","2","7","0","3","1","2","1","2","3","1","1","49","2","0","0","0","2","2" +"17032010190000","2016","211",1887,3299,1204,8,11,3,0,2,3,"2","2","2","3","2","2","7","0","3","1","2","2","2","1","6","2","128","2","0","0","0","1","2" +"09361030150000","2016","203",1928,1200,5280,2,5,1,0,0,1,"2","6","0","3","1","1","1","1","1","1","3","2","2","3","1","1","87","3","0","0","0","2","0" +"09361030150000","2015","203",1928,1200,5280,2,5,1,0,0,1,"2","6","0","3","1","1","1","1","1","1","3","2","2","3","1","1","87","3","0","0","0","2","0" +"17032010190000","2015","211",1887,3299,1204,8,11,3,0,2,3,"2","2","2","3","2","2","7","0","3","1","2","2","2","1","6","2","128","2","0","0","0","1","2" +"10253190450000","2021","209",1954,6027,11160,5,12,4,1,1,2,"2","6","2","3","1","2","3","2","3","3","4","1","2","3","1","1","70","2","0","0","0","1","2" +"09254040180000","2018","203",1955,1571,3750,3,6,1,0,0,5,"2","6","2","1","2","2","7","0","1","1","2","1","2","3","1","1","63","2","0","0","0","1","2" +"09254040180000","2017","203",1955,1571,3750,3,6,1,0,0,5,"2","6","2","1","2","2","7","0","1","1","2","1","2","3","1","1","60","2","0","0","0","1","2" +"13362270230000","2016","202",1890,756,2750,2,4,1,1,0,1,"2","6","0","3","2","2","3","1","1","1","3","2","3","1","1","1","125","2","0","0","1","2","2" +"13253230040000","2021","278",1972,2210,3150,4,9,3,0,0,2,"2","6","2","3","2","2","7","0","3","1","2","1","2","1","1","1","52","2","0","0","0","1","2" +"09254040180000","2015","203",1955,1571,3750,3,6,1,0,0,5,"2","6","2","1","2","2","7","0","1","1","2","1","2","3","1","1","60","2","0","0","0","1","2" +"13362270230000","2017","202",1890,756,2750,2,4,1,1,0,1,"2","6","0","3","2","2","3","1","1","1","3","2","3","1","1","1","125","2","0","0","1","2","2" +"13253230040000","2015","203",1969,1040,3150,2,5,1,0,0,1,"2","6","0","3","2","2","3","1","3","2","2","1","2","3","1","1","46","2","0","0","0","2","2" +"13253160160000","2019","202",1901,706,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","1","1","2","2","2","3","1","1","117","2","0","0","0","2","2" +"10254170360000","2017","205",1948,1829,4340,3,7,2,1,2,2,"2","6","2","3","2","2","3","1","3","1","2","1","2","1","1","1","67","2","0","0","0","1","2" +"10253190450000","2019","204",1951,2469,11160,3,8,2,1,1,1,"2","6","0","3","1","2","3","2","3","3","2","1","2","3","1","1","67","2","0","0","0","1","2" +"14321260280000","2021","206",1880,3660,3125,4,11,5,0,0,3,"2","6","2","3","2","2","4","2","3","3","2","1","2","1","2","1","143","2","0","1","0","2","2" +"09361030150000","2018","203",1928,1357,5280,2,6,1,1,0,1,"2","6","2","3","2","2","1","1","3","1","2","2","2","3","1","1","90","3","0","0","0","2","2" +"09361030150000","2021","206",1931,2772,5280,5,9,3,1,0,2,"2","6","2","3","2","2","1","1","3","1","3","2","2","3","1","1","93","3","0","0","0","1","2" +"17032010190000","2021","210",1887,3299,1204,8,11,3,1,2,3,"2","6","2","3","2","2","7","0","3","1","2","2","2","1","6","1","134","2","0","1","0","1","2" +"13253160160000","2021","205",1904,1790,3150,3,7,3,0,0,2,"2","6","2","3","2","2","3","1","1","1","2","2","2","1","1","1","120","2","0","1","0","1","2" +"10254170360000","2021","206",1959,2312,4340,4,8,3,1,2,2,"2","6","2","3","2","2","3","1","3","1","2","1","2","1","1","1","73","2","0","0","0","1","2" +"13362270230000","2020","202",1890,756,2750,2,4,1,1,0,1,"2","6","0","3","2","2","3","1","1","1","3","2","3","1","1","1","128","2","0","0","1","2","2" +"09361030150000","2017","203",1928,1357,5280,2,6,1,1,0,1,"2","6","2","3","2","2","1","1","3","1","2","2","2","3","1","1","87","3","0","0","0","2","2" +"17032010190000","2017","211",1887,3299,1204,8,11,3,0,2,3,"2","2","2","3","2","2","7","0","3","1","2","2","2","1","6","2","128","2","0","0","0","1","2" +"13253230040000","2017","203",1969,1040,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","3","1","2","1","2","3","1","1","46","2","0","0","0","2","2" +"17032010190000","2019","211",1888,3299,1204,8,11,3,0,2,3,"2","2","2","3","2","2","7","0","3","1","2","2","2","1","6","2","131","2","0","0","0","1","2" +"09361030150000","2019","203",1928,1357,5280,2,6,1,1,0,1,"2","6","2","3","2","2","1","1","3","1","2","2","2","3","1","1","90","3","0","0","0","2","2" +"09363230550000","2015","203",1923,1200,4375,3,5,1,1,0,1,"2","6","2","3","2","2","7","0","3","1","3","2","2","3","1","1","92","2","0","0","1","2","2" +"09254040180000","2016","203",1955,1571,3750,3,6,1,0,0,5,"2","6","2","1","2","2","7","0","1","1","2","1","2","3","1","1","60","2","0","0","0","1","2" +"13362270230000","2019","202",1890,756,2750,2,4,1,1,0,1,"2","6","0","3","2","2","3","1","1","1","3","2","3","1","1","1","128","2","0","0","1","2","2" +"13253160160000","2018","202",1901,706,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","1","1","2","2","2","3","1","1","117","2","0","0","0","2","2" +"09363230550000","2021","206",1926,2376,4375,4,7,3,1,0,2,"2","6","2","3","2","2","3","1","3","1","3","2","2","3","1","1","98","2","0","0","1","1","2" +"13253230040000","2016","203",1969,1040,3150,2,5,1,0,0,1,"2","6","0","3","2","2","3","1","3","2","2","1","2","3","1","1","46","2","0","0","0","2","2" +"10254170360000","2016","205",1948,1775,4340,4,7,2,1,2,2,"2","6","2","3","2","2","7","0","3","1","2","1","2","1","1","1","67","2","0","0","0","1","2" +"13362270230000","2021","205",1893,1476,2750,4,6,3,1,0,2,"2","6","2","3","2","2","3","1","1","1","3","2","3","1","1","1","131","2","0","1","1","1","2" +"17032010190000","2018","211",1887,3299,1204,8,11,3,0,2,3,"2","2","2","3","2","2","7","0","3","1","2","2","2","1","6","2","131","2","0","0","0","1","2" +"14321260280000","2015","211",1878,2850,3125,6,15,3,0,0,3,"2","2","0","3","2","2","7","0","3","1","2","2","2","3","2","2","137","2","0","0","1","2","2" +"17032010190000","2020","211",1888,3299,1204,8,11,3,0,2,3,"2","2","2","3","2","2","7","0","3","1","2","2","2","1","6","2","131","2","0","0","0","1","2" +"13253160160000","2015","202",1901,706,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","1","1","2","2","2","3","1","1","114","2","0","0","1","2","2" +"09363230550000","2018","203",1923,1200,4375,3,5,1,1,0,1,"2","6","2","3","2","2","7","0","3","1","3","2","2","3","1","1","95","2","0","0","1","2","2" +"09254040180000","2019","203",1955,1571,3750,3,6,1,0,0,5,"2","6","2","1","2","2","7","0","1","1","2","1","2","3","1","1","63","2","0","0","0","1","2" +"10253190450000","2020","204",1951,2469,11160,3,8,2,1,1,1,"2","6","0","3","1","2","3","2","3","3","2","1","2","3","1","1","67","2","0","0","0","1","2" +"09361030150000","2020","203",1928,1357,5280,2,6,1,1,0,1,"2","6","2","3","2","2","1","1","3","1","2","2","2","3","1","1","90","3","0","0","0","2","2" +"13362270230000","2018","202",1890,756,2750,2,4,1,1,0,1,"2","6","0","3","2","2","3","1","1","1","3","2","3","1","1","1","128","2","0","0","1","2","2" +"10253190450000","2015","204",1951,2469,11160,3,8,2,1,1,1,"2","6","0","3","1","2","3","2","3","3","2","1","2","3","1","1","64","2","0","0","0","1","2" +"13253230040000","2018","203",1969,1040,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","3","1","2","1","2","3","1","1","49","2","0","0","0","2","2" +"10253190450000","2016","204",1951,2469,11160,3,8,2,1,1,1,"2","6","0","3","1","2","3","2","3","3","2","1","2","3","1","1","64","2","0","0","0","1","2" +"14321260280000","2019","211",1878,2850,3125,4,9,3,0,0,3,"2","2","2","3","2","2","7","0","3","3","2","1","2","1","2","2","140","2","0","0","0","2","2" +"13253160160000","2016","202",1901,706,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","1","1","2","2","2","3","1","1","114","2","0","0","0","2","2" +"09363230550000","2020","203",1923,1200,4375,3,5,1,1,0,1,"2","6","2","3","2","2","7","0","3","1","3","2","2","3","1","1","95","2","0","0","1","2","2" +"10254170360000","2019","205",1948,1829,4340,3,7,2,1,2,2,"2","6","2","3","2","2","3","1","3","1","2","1","2","1","1","1","70","2","0","0","0","1","2" +"13253230040000","2020","203",1969,1040,3150,2,5,1,0,0,1,"2","6","2","3","2","2","7","0","3","1","2","1","2","3","1","1","49","2","0","0","0","2","2" +"10254170360000","2020","205",1948,1829,4340,3,7,2,1,2,2,"2","6","2","3","2","2","3","1","3","1","2","1","2","1","1","1","70","2","0","0","0","1","2" +"09254040180000","2021","203",1958,1568,3750,3,8,3,0,0,6,"2","6","2","1","2","2","3","1","1","1","2","1","2","1","1","1","66","2","0","0","0","1","2" +"14321260280000","2020","211",1878,2850,3125,4,9,3,0,0,3,"2","2","2","3","2","2","7","0","3","3","2","1","2","1","2","2","140","2","0","0","0","2","2" +"14321260280000","2017","211",1878,2850,3125,4,9,3,0,0,3,"2","2","2","3","2","2","7","0","3","3","2","1","2","1","2","2","137","2","0","0","0","2","2" diff --git a/python/tests/fixtures/chars_sample_hie.csv b/python/tests/fixtures/chars_sample_hie.csv new file mode 100755 index 0000000..f618a68 --- /dev/null +++ b/python/tests/fixtures/chars_sample_hie.csv @@ -0,0 +1,12 @@ +"pin","qu_town","qu_mlt_cd","qu_home_improvement","qu_use","qu_exterior_wall","qu_roof","qu_basement_type","qu_basement_finish","qu_heat","qu_air","qu_attic_type","qu_attic_finish","qu_type_plan","qu_type_design","qu_construct_quality","qu_porch","qu_garage_size","qu_garage_const","qu_garage_attached","qu_garage_area","qu_num_apts","qu_sqft_bld","qu_lnd_sqft","qu_class","qu_rooms","qu_beds","qu_full_bath","qu_half_bath","qu_fire_place","qu_no_com_unit","qu_type_of_res","qu_upload_date","hie_last_year_active","year" +"13253230040000","77","4","1","0","0","0","0","0","0","0","0","0","0","0","0","0","3","1","2","2","0",0,0,"203",0,0,0,0,0,0,"0",2019-06-19,"2023","2019" +"10254170360000","75","3","1","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0",483,0,"206",1,1,1,0,0,0,"0",2017-01-23,"2020","2017" +"13253230040000","77","3","1","0","0","0","1","1","0","1","0","0","0","0","0","0","0","0","0","0","0",1170,0,"278",4,2,2,0,0,0,"2",2017-04-20,"2020","2017" +"09361030150000","71","3","1","0","3","0","0","0","0","1","0","0","0","0","0","0","0","0","0","0","0",1415,0,"206",3,3,2,0,0,0,"2",2017-06-09,"2020","2017" +"13362270230000","77","3","1","0","0","0","0","0","0","1","0","0","0","0","0","0","0","0","0","0","0",720,0,"205",2,2,2,0,0,0,"2",2015-07-01,"2020","2015" +"09254040180000","71","3","1","0","0","0","0","0","0","1","0","0","0","0","0","0","3","1","2","2","0",845,0,"207",2,2,2,0,0,0,"2",2015-08-01,"2020","2015" +"09363230550000","71","3","1","0","0","0","0","0","0","1","0","0","0","0","0","0","3","1","2","2","0",1176,0,"206",2,1,2,0,0,0,"2",2015-08-01,"2020","2015" +"17032010190000","74","3","1","1","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","6",0,0,"210",0,0,0,1,0,0,"0",2015-09-02,"2020","2015" +"10253190450000","75","3","1","0","4","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0",3558,0,"209",4,2,2,0,0,0,"2",2016-02-03,"2020","2016" +"13253160160000","77","3","1","0","0","0","1","1","0","1","0","0","0","0","0","0","0","0","0","0","0",1084,0,"205",2,1,2,0,0,0,"2",2016-08-01,"2020","2016" +"14321260280000","74","3","1","1","0","0","0","0","0","0","0","0","0","0","0","0","4","2","2","2","6",810,0,"206",2,0,2,0,0,0,"0",2016-09-13,"2020","2016" diff --git a/python/tests/test_vars_funs.py b/python/tests/test_vars_funs.py new file mode 100644 index 0000000..776cc50 --- /dev/null +++ b/python/tests/test_vars_funs.py @@ -0,0 +1,172 @@ +import pandas as pd +import pytest + +import ccao +import ccao.vars_funs + + +@pytest.mark.parametrize("output_type", ["inplace", "vector"]) +def test_vars_rename_input_data_is_dataframe(output_type, chars_sample_athena): + data = chars_sample_athena.iloc[:, 13:19].copy() + result = ccao.vars_rename( + data=data, names_from="athena", names_to="pretty", output_type=output_type + ) + expected = [ + "Apartments", + "Cathedral Ceiling", + "Attic Finish", + "Garage 1 Attached", + "Garage 1 Area Included", + "Garage 1 Size", + ] + if output_type == "inplace": + assert list(result.columns) == expected + else: + assert result == expected + + +@pytest.mark.parametrize("output_type", ["inplace", "vector"]) +def test_vars_rename_input_data_is_list(output_type): + result = ccao.vars_rename( + data=["Apartments", "Cathedral Ceiling"], + names_from="pretty", + names_to="model", + output_type=output_type, + ) + expected = ["char_apts", "char_tp_dsgn"] + # Output should be the same regardless of the value of `output_type` + assert result == expected + + +def test_vars_rename_hie_to_athena(chars_sample_hie): + data = chars_sample_hie.iloc[:, 1:3].copy() + result = ccao.vars_rename( + data=data, names_from="hie", names_to="athena", output_type="vector" + ) + expected = ["township_code", "card"] + assert result == expected + + +def test_vars_rename_unmatched_cols_unchanged(): + # If columns are not present in the dictionary, leave them as-is + unmatched_colnames = ["foo", "bar", "baz"] + result = ccao.vars_rename( + data=unmatched_colnames, names_from="hie", names_to="athena" + ) + assert result == unmatched_colnames + + +@pytest.mark.parametrize("output_type", ["vector", ccao.vars_funs.OutputType.VECTOR]) +def test_vars_rename_output_type_enum_or_string(output_type, chars_sample_athena): + # Both the enum and string versions of output_type should work + result = ccao.vars_rename( + data=chars_sample_athena.iloc[:, 13:19], + names_from="athena", + names_to="pretty", + output_type=output_type, + ) + expected = [ + "Apartments", + "Cathedral Ceiling", + "Attic Finish", + "Garage 1 Attached", + "Garage 1 Area Included", + "Garage 1 Size", + ] + assert result == expected + + +def test_vars_rename_custom_dictionary(): + result = ccao.vars_rename( + data=["1", "2", "3"], + names_from="foo", + names_to="bar", + dictionary=pd.DataFrame( + { + "var_name_foo": ["1", "2", "3"], + "var_name_bar": ["char_1", "char_2", "char_3"], + } + ), + ) + expected = ["char_1", "char_2", "char_3"] + assert result == expected + + +def test_vars_rename_invalid_dictionary_type(): + with pytest.raises(ValueError) as exc: + ccao.vars_rename( + data=["1", "2", "3"], + names_from="sql", + names_to="char", + dictionary={"sql": "char"}, + ) + assert "dictionary must be" in str(exc.value) + + +def test_vars_rename_invalid_dictionary_empty(): + with pytest.raises(ValueError) as exc: + ccao.vars_rename( + data=["1", "2", "3"], + names_from="sql", + names_to="char", + dictionary=pd.DataFrame(), + ) + assert "non-empty" in str(exc.value) + + +def test_vars_rename_invalid_dictionary_missing_variable_columns(): + with pytest.raises(ValueError) as exc: + ccao.vars_rename( + data=["1", "2", "3"], + names_from="foo", + names_to="bar", + dictionary=pd.DataFrame( + { + "foo": ["1", "2", "3"], + "bar": ["char_1", "char_2", "char_3"], + } + ), + ) + assert f"starting with {ccao.vars_funs.var_name_prefix}" in str(exc.value) + + +@pytest.mark.parametrize("names_from,names_to", [(1, "pretty"), ("pretty", 1)]) +def test_vars_rename_invalid_names_type(names_from, names_to): + with pytest.raises(ValueError) as exc: + ccao.vars_rename(data=["1", "2", "3"], names_from=names_from, names_to=names_to) + assert "must be strings" in str(exc.value) + + +@pytest.mark.parametrize("names_from,names_to", [("1", "pretty"), ("pretty", "1")]) +def test_vars_rename_invalid_names_value(names_from, names_to): + with pytest.raises(ValueError) as exc: + ccao.vars_rename(data=["1", "2", "3"], names_from=names_from, names_to=names_to) + assert "must be one of" in str(exc.value) + + +def test_vars_rename_invalid_output_type_type(): + with pytest.raises(ValueError) as exc: + ccao.vars_rename( + data=["Apartments", "Cathedral Ceiling"], + names_from="pretty", + names_to="model", + output_type=1, + ) + assert "output_type must be a string or" in str(exc.value) + + +def test_vars_rename_invalid_output_type_value(): + with pytest.raises(ValueError) as exc: + ccao.vars_rename( + data=["Apartments", "Cathedral Ceiling"], + names_from="pretty", + names_to="model", + output_type="foo", + ) + assert "output_type must be one of" in str(exc.value) + + +def test_vars_rename_invalid_data_type(): + with pytest.raises(TypeError) as exc: + ccao.vars_rename(data=1, names_from="athena", names_to="pretty") + assert str(exc.value).startswith("data must be") From adb87a68fde0496cdb6a03dd3b400a24d890199e Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 20 Nov 2024 16:26:10 -0600 Subject: [PATCH 03/59] Add pytest-coverage workflow --- .github/workflows/pytest-coverage.yaml | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/pytest-coverage.yaml diff --git a/.github/workflows/pytest-coverage.yaml b/.github/workflows/pytest-coverage.yaml new file mode 100644 index 0000000..ce8757b --- /dev/null +++ b/.github/workflows/pytest-coverage.yaml @@ -0,0 +1,50 @@ +on: + pull_request: + push: + branches: [main, master] + +name: pytest-coverage + +env: + PYTHONUNBUFFERED: "1" + UV_SYSTEM_PYTHON: 1 + +jobs: + pytest-coverage: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: python/pyproject.toml + cache-suffix: test + + - name: Install Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version-file: ${{ matrix.python-version }} + + - name: Install Python dependencies + working-directory: python + shell: bash + run: uv pip install .[dev] + + - name: Run pytest + run: | + pytest --doctest-modules \ + --junitxml=junit/test-results-${{ matrix.python-version }}.xmlpytest + + - name: Upload artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: pytest-results-${{ matrix.python-version }} + path: junit/test-results-${{ matrix.python-version }}.xml From c0739e829c16325bfe81077bb6c6cabb74ff6931 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 20 Nov 2024 16:31:49 -0600 Subject: [PATCH 04/59] Add Development docs to python/README.md --- .pre-commit-config.yaml | 1 + python/README.md | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c1e3684..f4e0385 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,6 +10,7 @@ repos: - id: use-tidy-description - id: lintr - id: readme-rmd-rendered + exclude: ^python/ - id: parsable-R - id: no-browser-statement - id: no-debug-statement diff --git a/python/README.md b/python/README.md index ca9680b..5718aa9 100644 --- a/python/README.md +++ b/python/README.md @@ -5,7 +5,7 @@ package](https://ccao-data.github.io/ccao/), providing utilities for managing, distributing, and version controlling *CCAO-specific* functions used throughout CCAO applications, models, and diagnostics. For generalized versions of assessment-related functions, see -[assesspy](https://github.com/ccao-data/assesspy) +[assesspy](https://github.com/ccao-data/assesspy). ## Installation @@ -14,3 +14,22 @@ You can install the released version of `ccao` directly from GitHub: ```bash pip install "git+https://github.com/ccao-data/ccao.git#egg=ccao&subdirectory=python" ``` + +## Development + +Create a development environment using [`uv`](https://docs.astral.sh/uv/): + +``` +uv venv +source .venv/bin/activate +uv python install +uv pip install .[dev] +``` + +### Running tests + +Run tests with pytest: + +``` +uv run pytest +``` From 6d63b5127daceefcd455d2e31005bf193aab2470 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 20 Nov 2024 16:36:33 -0600 Subject: [PATCH 05/59] Clean up docs in vars_funs.py --- python/ccao/vars_funs.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index dcb6b19..b0dd851 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -1,3 +1,4 @@ +# Functions for translating variables between different data sources import enum import importlib.resources @@ -37,9 +38,10 @@ def vars_rename( This function renames columns in a dataset based on a dictionary that maps names from one convention to another. It can rename columns in-place or return - a character vector of renamed columns. + a character vector of renamed columns, behavior that is configurable using + the `output_type` argument. - :param data: DataFrame or list of column names. + :param data: DataFrame or list of column names to rename. The input data with columns to be renamed. If a DataFrame, renames columns directly. :type data: pandas.DataFrame or list[str] @@ -51,7 +53,7 @@ def vars_rename( Must match a key in the dictionary. :type names_to: str - :param output_type: Output type. Either `"inplace"`, which renames the + :param output_type: Output type. Either `"inplace"`, which mutates the input data frame, or `"vector"`, which returns a list of strings with the construction new_col_name = old_col_name. :type output_type: OutputType or str @@ -121,7 +123,7 @@ def vars_rename( to = f"{var_name_prefix}_{names_to}" mapping = dict(zip(dictionary[from_], dictionary[to])) - # Handle output differently depending on the input type and `inplace` kwarg + # Handle output differently depending on the input and output type args if isinstance(data, pd.DataFrame): if output_type == OutputType.INPLACE: data.rename(columns=mapping, inplace=True) From f3f38e1f43a5c85af5ac0985f1efb85fc4520a65 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 20 Nov 2024 16:43:02 -0600 Subject: [PATCH 06/59] Fix typo in pytest-coverage workflow --- .github/workflows/pytest-coverage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pytest-coverage.yaml b/.github/workflows/pytest-coverage.yaml index ce8757b..fc82337 100644 --- a/.github/workflows/pytest-coverage.yaml +++ b/.github/workflows/pytest-coverage.yaml @@ -30,7 +30,7 @@ jobs: - name: Install Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: - python-version-file: ${{ matrix.python-version }} + python-version: ${{ matrix.python-version }} - name: Install Python dependencies working-directory: python From abdeca04534b2471ff376826235c79ac0a8fdd33 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 20 Nov 2024 16:50:55 -0600 Subject: [PATCH 07/59] Accept any python >=3.9 in python package --- python/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/pyproject.toml b/python/pyproject.toml index 322e256..094c8eb 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -3,7 +3,7 @@ name = "ccao" version = "1.3.0" description = "Convenience Functions and Datasets for the Cook County Assessor's Office" readme = "README.md" -requires-python = ">=3.11" +requires-python = ">=3.9" authors = [ {name = "Dan Snow", email="daniel.snow@cookcountyil.gov"}, {name = "Jean Cochrane", email="jean.cochrane@cookcountyil.gov"}, From ec0a63e113ca0210391de9fc3c3f2972986e9354 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 20 Nov 2024 16:52:38 -0600 Subject: [PATCH 08/59] Use optional-dependencies for dev deps in pyproject.toml --- python/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/pyproject.toml b/python/pyproject.toml index 094c8eb..44e0024 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -12,7 +12,7 @@ dependencies = [ "pandas>=2.2.3", ] -[dependency-groups] +[project.optional-dependencies] dev = [ "mypy>=1.13.0", "pytest>=8.3.3", From 08e418a76b532c4f6dab30242ce8b4bfc78fff4f Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 20 Nov 2024 16:57:55 -0600 Subject: [PATCH 09/59] Fix vars_rename docstring in Python package --- python/ccao/vars_funs.py | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index b0dd851..f5096f9 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -41,37 +41,53 @@ def vars_rename( a character vector of renamed columns, behavior that is configurable using the `output_type` argument. - :param data: DataFrame or list of column names to rename. - The input data with columns to be renamed. If a DataFrame, renames columns directly. + :param data: + DataFrame or list of column names to rename. + If a DataFrame, renames columns directly. :type data: pandas.DataFrame or list[str] - :param names_from: The source naming convention to rename from. + :param names_from: + The source naming convention to rename from. Must match a key in the dictionary. :type names_from: str - :param names_to: The target naming convention to rename to. + :param names_to: + The target naming convention to rename to. Must match a key in the dictionary. :type names_to: str - :param output_type: Output type. Either `"inplace"`, which mutates the - input data frame, or `"vector"`, which returns a list of strings with - the construction new_col_name = old_col_name. + :param output_type: + Output type. Either `"inplace"`, which mutates the input data frame, + or `"vector"`, which returns a list of strings with the construction + new_col_name = old_col_name. :type output_type: OutputType or str - :param dictionary: The dictionary for mapping column names. + :param dictionary: + The dictionary for mapping column names. Must contain keys like `var_name_` and `var_name_`. :type dictionary: pandas.DataFrame :raises ValueError: If required arguments are invalid or the dictionary does not meet format requirements. :raises TypeError: If `data` is neither a DataFrame nor a list of column names. - :return: Either the input data with renamed columns if `output_type` is + :return: + Either the input data with renamed columns if `output_type` is `"inplace"` and the input data is a DataFrame, otherwise a list of renamed columns. :rtype: pandas.DataFrame or list[str] :example: - >>> vars_rename(data=sample_data, names_from="model", names_to="athena", output_type="inplace") + + .. code-block:: python + + import ccao + + ccao.vars_rename( + data=["char_yrblt"], + names_from="athena", + names_to="pretty", + output_type="vector" + ) """ # Validate the dictionary schema dictionary = dictionary if dictionary is not None else vars_dict From 001b0793f0515a6a7cc45a4586c19e6981b0d8f5 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 20 Nov 2024 17:01:32 -0600 Subject: [PATCH 10/59] Update typing in vars_funs.py to be compatible with Python 3.9 --- python/ccao/vars_funs.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index f5096f9..1614470 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -1,6 +1,7 @@ # Functions for translating variables between different data sources import enum import importlib.resources +import typing import pandas as pd @@ -27,12 +28,12 @@ def values(cls) -> list[str]: def vars_rename( - data: list[str] | pd.DataFrame, + data: typing.Union[list[str], pd.DataFrame], names_from: str, names_to: str, - output_type: OutputType | str = OutputType.INPLACE, - dictionary: pd.DataFrame | None = None, -) -> list[str] | pd.DataFrame: + output_type: typing.Union[OutputType, str] = OutputType.INPLACE, + dictionary: typing.Union[pd.DataFrame, None] = None, +) -> typing.Union[list[str], pd.DataFrame]: """ Rename variables from one naming convention to another. From 2aae031b68f454258e446f4c1083bd807db2a7f4 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 16:04:45 -0600 Subject: [PATCH 11/59] Add Sphinx docs for Python package --- .github/workflows/pytest-coverage.yaml | 2 +- python/.gitignore | 3 + python/README.md | 14 +- python/docs/images/logo.png | 1 + python/docs/source/authors.rst | 7 + python/docs/source/conf.py | 13 + python/docs/source/index.rst | 33 ++ python/docs/source/license.rst | 5 + python/docs/source/reference.rst | 14 + python/docs/source/vars_rename.rst | 5 + python/pyproject.toml | 36 +- python/uv.lock | 586 ++++++++++++++++++++++++- 12 files changed, 704 insertions(+), 15 deletions(-) create mode 120000 python/docs/images/logo.png create mode 100644 python/docs/source/authors.rst create mode 100644 python/docs/source/conf.py create mode 100644 python/docs/source/index.rst create mode 100644 python/docs/source/license.rst create mode 100644 python/docs/source/reference.rst create mode 100644 python/docs/source/vars_rename.rst diff --git a/.github/workflows/pytest-coverage.yaml b/.github/workflows/pytest-coverage.yaml index fc82337..183d0ec 100644 --- a/.github/workflows/pytest-coverage.yaml +++ b/.github/workflows/pytest-coverage.yaml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13"] steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/python/.gitignore b/python/.gitignore index f0b0438..bec2db9 100644 --- a/python/.gitignore +++ b/python/.gitignore @@ -34,3 +34,6 @@ venv/ ENV/ env.bak/ venv.bak/ + +# Sphinx documentation +_build/ diff --git a/python/README.md b/python/README.md index 5718aa9..95260e5 100644 --- a/python/README.md +++ b/python/README.md @@ -23,7 +23,7 @@ Create a development environment using [`uv`](https://docs.astral.sh/uv/): uv venv source .venv/bin/activate uv python install -uv pip install .[dev] +uv pip install .[test] ``` ### Running tests @@ -31,5 +31,15 @@ uv pip install .[dev] Run tests with pytest: ``` -uv run pytest +pytest ``` + +### Building docs + +Build and serve the docs locally with sphinx: + +``` +sphinx-autobuild docs/source _build/html +``` + +Navigate to http://localhost:8000 to view the docs. diff --git a/python/docs/images/logo.png b/python/docs/images/logo.png new file mode 120000 index 0000000..1b1dd05 --- /dev/null +++ b/python/docs/images/logo.png @@ -0,0 +1 @@ +../../../man/figures/logo.png \ No newline at end of file diff --git a/python/docs/source/authors.rst b/python/docs/source/authors.rst new file mode 100644 index 0000000..c0c6217 --- /dev/null +++ b/python/docs/source/authors.rst @@ -0,0 +1,7 @@ +======= +Authors +======= + +**Jean Cochrane**. Author, maintainer. + +**Dan Snow**. Author, maintainer. diff --git a/python/docs/source/conf.py b/python/docs/source/conf.py new file mode 100644 index 0000000..60c1c59 --- /dev/null +++ b/python/docs/source/conf.py @@ -0,0 +1,13 @@ +import os +import sys + +from sphinx_pyproject import SphinxConfig + +# Add source path to sys path so that autodoc can load functions +sys.path.append(os.path.abspath("../..")) + +# Loads config from pyproject.toml +config = SphinxConfig("../../pyproject.toml", globalns=globals()) + +# Options that can't be parsed by sphinx-pyproject +html_sidebars = {"**": []} diff --git a/python/docs/source/index.rst b/python/docs/source/index.rst new file mode 100644 index 0000000..ffba847 --- /dev/null +++ b/python/docs/source/index.rst @@ -0,0 +1,33 @@ +CCAO Python package +=================== + +.. toctree:: + :hidden: + :caption: Contents: + + reference + +.. toctree:: + :hidden: + :caption: Appendix: + + authors + license + Source Code + +This is a Python version of the ``ccao`` `R +package `_, providing utilities for +managing, distributing, and version controlling CCAO-specific functions +used throughout CCAO applications, models, and diagnostics. For generalized +versions of assessment-related functions, see +`assesspy `_. + + +Installation +------------ + +You can install the released version of ``ccao`` directly from GitHub: + +.. code-block:: python + + pip install "git+https://github.com/ccao-data/ccao.git#egg=ccao&subdirectory=python" diff --git a/python/docs/source/license.rst b/python/docs/source/license.rst new file mode 100644 index 0000000..546489a --- /dev/null +++ b/python/docs/source/license.rst @@ -0,0 +1,5 @@ +======= +License +======= + +.. literalinclude:: ../../../LICENSE diff --git a/python/docs/source/reference.rst b/python/docs/source/reference.rst new file mode 100644 index 0000000..3dc531e --- /dev/null +++ b/python/docs/source/reference.rst @@ -0,0 +1,14 @@ +========= +Reference +========= + +Functions +--------- + +Manage characteristics +^^^^^^^^^^^^^^^^^^^^^^ + +Recode/rename characteristic columns, merge HIE data, and fix characteristic +errors. + +:doc:`vars_rename() ` diff --git a/python/docs/source/vars_rename.rst b/python/docs/source/vars_rename.rst new file mode 100644 index 0000000..db93a1d --- /dev/null +++ b/python/docs/source/vars_rename.rst @@ -0,0 +1,5 @@ +================================================================================== +Bulk rename variables from CCAO SQL to standardized or pretty names and visa versa +================================================================================== + +.. autofunction:: ccao.vars_rename diff --git a/python/pyproject.toml b/python/pyproject.toml index 44e0024..2fcb2dd 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -3,7 +3,7 @@ name = "ccao" version = "1.3.0" description = "Convenience Functions and Datasets for the Cook County Assessor's Office" readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ {name = "Dan Snow", email="daniel.snow@cookcountyil.gov"}, {name = "Jean Cochrane", email="jean.cochrane@cookcountyil.gov"}, @@ -13,8 +13,40 @@ dependencies = [ ] [project.optional-dependencies] -dev = [ +test = [ "mypy>=1.13.0", "pytest>=8.3.3", "ruff>=0.7.4", ] +docs = [ + "Sphinx>=8.1.3", + "myst-parser>=4.0.0", + "pydata-sphinx-theme>=0.16.0", + "sphinx-pyproject>=0.3.0" +] + +[tool.ruff] +line-length = 79 + +[tool.ruff.lint] +extend-select = ["I"] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401"] + +[tool.sphinx-pyproject] +github_username = "ccao-data" +github_repository = "ccao" +project = "ccao" +copyright = "2024, Cook County Assessor's Office" +language = "en" +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "myst_parser" +] +# templates_path = ["_templates"] +highlight_language = "none" +html_theme = "pydata_sphinx_theme" +html_logo = "../images/logo.png" +html_show_copyright = false diff --git a/python/uv.lock b/python/uv.lock index eb2f398..3323c03 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -1,10 +1,53 @@ version = 1 -requires-python = ">=3.11" +requires-python = ">=3.10" resolution-markers = [ - "python_full_version < '3.12'", + "python_full_version < '3.11'", + "python_full_version == '3.11.*'", "python_full_version >= '3.12'", ] +[[package]] +name = "accessible-pygments" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903 }, +] + +[[package]] +name = "alabaster" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, +] + +[[package]] +name = "babel" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 }, +] + [[package]] name = "ccao" version = "1.3.0" @@ -13,21 +56,107 @@ dependencies = [ { name = "pandas" }, ] -[package.dev-dependencies] -dev = [ +[package.optional-dependencies] +docs = [ + { name = "myst-parser" }, + { name = "pydata-sphinx-theme" }, + { name = "sphinx" }, + { name = "sphinx-pyproject" }, +] +test = [ { name = "mypy" }, { name = "pytest" }, { name = "ruff" }, ] [package.metadata] -requires-dist = [{ name = "pandas", specifier = ">=2.2.3" }] +requires-dist = [ + { name = "mypy", marker = "extra == 'test'", specifier = ">=1.13.0" }, + { name = "myst-parser", marker = "extra == 'docs'", specifier = ">=4.0.0" }, + { name = "pandas", specifier = ">=2.2.3" }, + { name = "pydata-sphinx-theme", marker = "extra == 'docs'", specifier = ">=0.16.0" }, + { name = "pytest", marker = "extra == 'test'", specifier = ">=8.3.3" }, + { name = "ruff", marker = "extra == 'test'", specifier = ">=0.7.4" }, + { name = "sphinx", marker = "extra == 'docs'", specifier = ">=8.1.3" }, + { name = "sphinx-pyproject", marker = "extra == 'docs'", specifier = ">=0.3.0" }, +] -[package.metadata.requires-dev] -dev = [ - { name = "mypy", specifier = ">=1.13.0" }, - { name = "pytest", specifier = ">=8.3.3" }, - { name = "ruff", specifier = ">=0.7.4" }, +[[package]] +name = "certifi" +version = "2024.8.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", size = 106620 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/8b/825cc84cf13a28bfbcba7c416ec22bf85a9584971be15b21dd8300c65b7f/charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", size = 196363 }, + { url = "https://files.pythonhosted.org/packages/23/81/d7eef6a99e42c77f444fdd7bc894b0ceca6c3a95c51239e74a722039521c/charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", size = 125639 }, + { url = "https://files.pythonhosted.org/packages/21/67/b4564d81f48042f520c948abac7079356e94b30cb8ffb22e747532cf469d/charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", size = 120451 }, + { url = "https://files.pythonhosted.org/packages/c2/72/12a7f0943dd71fb5b4e7b55c41327ac0a1663046a868ee4d0d8e9c369b85/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", size = 140041 }, + { url = "https://files.pythonhosted.org/packages/67/56/fa28c2c3e31217c4c52158537a2cf5d98a6c1e89d31faf476c89391cd16b/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", size = 150333 }, + { url = "https://files.pythonhosted.org/packages/f9/d2/466a9be1f32d89eb1554cf84073a5ed9262047acee1ab39cbaefc19635d2/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", size = 142921 }, + { url = "https://files.pythonhosted.org/packages/f8/01/344ec40cf5d85c1da3c1f57566c59e0c9b56bcc5566c08804a95a6cc8257/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", size = 144785 }, + { url = "https://files.pythonhosted.org/packages/73/8b/2102692cb6d7e9f03b9a33a710e0164cadfce312872e3efc7cfe22ed26b4/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", size = 146631 }, + { url = "https://files.pythonhosted.org/packages/d8/96/cc2c1b5d994119ce9f088a9a0c3ebd489d360a2eb058e2c8049f27092847/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", size = 140867 }, + { url = "https://files.pythonhosted.org/packages/c9/27/cde291783715b8ec30a61c810d0120411844bc4c23b50189b81188b273db/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", size = 149273 }, + { url = "https://files.pythonhosted.org/packages/3a/a4/8633b0fc1a2d1834d5393dafecce4a1cc56727bfd82b4dc18fc92f0d3cc3/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", size = 152437 }, + { url = "https://files.pythonhosted.org/packages/64/ea/69af161062166b5975ccbb0961fd2384853190c70786f288684490913bf5/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", size = 150087 }, + { url = "https://files.pythonhosted.org/packages/3b/fd/e60a9d9fd967f4ad5a92810138192f825d77b4fa2a557990fd575a47695b/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", size = 145142 }, + { url = "https://files.pythonhosted.org/packages/6d/02/8cb0988a1e49ac9ce2eed1e07b77ff118f2923e9ebd0ede41ba85f2dcb04/charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", size = 94701 }, + { url = "https://files.pythonhosted.org/packages/d6/20/f1d4670a8a723c46be695dff449d86d6092916f9e99c53051954ee33a1bc/charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", size = 102191 }, + { url = "https://files.pythonhosted.org/packages/9c/61/73589dcc7a719582bf56aae309b6103d2762b526bffe189d635a7fcfd998/charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", size = 193339 }, + { url = "https://files.pythonhosted.org/packages/77/d5/8c982d58144de49f59571f940e329ad6e8615e1e82ef84584c5eeb5e1d72/charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", size = 124366 }, + { url = "https://files.pythonhosted.org/packages/bf/19/411a64f01ee971bed3231111b69eb56f9331a769072de479eae7de52296d/charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", size = 118874 }, + { url = "https://files.pythonhosted.org/packages/4c/92/97509850f0d00e9f14a46bc751daabd0ad7765cff29cdfb66c68b6dad57f/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", size = 138243 }, + { url = "https://files.pythonhosted.org/packages/e2/29/d227805bff72ed6d6cb1ce08eec707f7cfbd9868044893617eb331f16295/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", size = 148676 }, + { url = "https://files.pythonhosted.org/packages/13/bc/87c2c9f2c144bedfa62f894c3007cd4530ba4b5351acb10dc786428a50f0/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", size = 141289 }, + { url = "https://files.pythonhosted.org/packages/eb/5b/6f10bad0f6461fa272bfbbdf5d0023b5fb9bc6217c92bf068fa5a99820f5/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", size = 142585 }, + { url = "https://files.pythonhosted.org/packages/3b/a0/a68980ab8a1f45a36d9745d35049c1af57d27255eff8c907e3add84cf68f/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", size = 144408 }, + { url = "https://files.pythonhosted.org/packages/d7/a1/493919799446464ed0299c8eef3c3fad0daf1c3cd48bff9263c731b0d9e2/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", size = 139076 }, + { url = "https://files.pythonhosted.org/packages/fb/9d/9c13753a5a6e0db4a0a6edb1cef7aee39859177b64e1a1e748a6e3ba62c2/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", size = 146874 }, + { url = "https://files.pythonhosted.org/packages/75/d2/0ab54463d3410709c09266dfb416d032a08f97fd7d60e94b8c6ef54ae14b/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", size = 150871 }, + { url = "https://files.pythonhosted.org/packages/8d/c9/27e41d481557be53d51e60750b85aa40eaf52b841946b3cdeff363105737/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", size = 148546 }, + { url = "https://files.pythonhosted.org/packages/ee/44/4f62042ca8cdc0cabf87c0fc00ae27cd8b53ab68be3605ba6d071f742ad3/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", size = 143048 }, + { url = "https://files.pythonhosted.org/packages/01/f8/38842422988b795220eb8038745d27a675ce066e2ada79516c118f291f07/charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", size = 94389 }, + { url = "https://files.pythonhosted.org/packages/0b/6e/b13bd47fa9023b3699e94abf565b5a2f0b0be6e9ddac9812182596ee62e4/charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", size = 101752 }, + { url = "https://files.pythonhosted.org/packages/d3/0b/4b7a70987abf9b8196845806198975b6aab4ce016632f817ad758a5aa056/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", size = 194445 }, + { url = "https://files.pythonhosted.org/packages/50/89/354cc56cf4dd2449715bc9a0f54f3aef3dc700d2d62d1fa5bbea53b13426/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", size = 125275 }, + { url = "https://files.pythonhosted.org/packages/fa/44/b730e2a2580110ced837ac083d8ad222343c96bb6b66e9e4e706e4d0b6df/charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", size = 119020 }, + { url = "https://files.pythonhosted.org/packages/9d/e4/9263b8240ed9472a2ae7ddc3e516e71ef46617fe40eaa51221ccd4ad9a27/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", size = 139128 }, + { url = "https://files.pythonhosted.org/packages/6b/e3/9f73e779315a54334240353eaea75854a9a690f3f580e4bd85d977cb2204/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", size = 149277 }, + { url = "https://files.pythonhosted.org/packages/1a/cf/f1f50c2f295312edb8a548d3fa56a5c923b146cd3f24114d5adb7e7be558/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", size = 142174 }, + { url = "https://files.pythonhosted.org/packages/16/92/92a76dc2ff3a12e69ba94e7e05168d37d0345fa08c87e1fe24d0c2a42223/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", size = 143838 }, + { url = "https://files.pythonhosted.org/packages/a4/01/2117ff2b1dfc61695daf2babe4a874bca328489afa85952440b59819e9d7/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", size = 146149 }, + { url = "https://files.pythonhosted.org/packages/f6/9b/93a332b8d25b347f6839ca0a61b7f0287b0930216994e8bf67a75d050255/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", size = 140043 }, + { url = "https://files.pythonhosted.org/packages/ab/f6/7ac4a01adcdecbc7a7587767c776d53d369b8b971382b91211489535acf0/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", size = 148229 }, + { url = "https://files.pythonhosted.org/packages/9d/be/5708ad18161dee7dc6a0f7e6cf3a88ea6279c3e8484844c0590e50e803ef/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", size = 151556 }, + { url = "https://files.pythonhosted.org/packages/5a/bb/3d8bc22bacb9eb89785e83e6723f9888265f3a0de3b9ce724d66bd49884e/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", size = 149772 }, + { url = "https://files.pythonhosted.org/packages/f7/fa/d3fc622de05a86f30beea5fc4e9ac46aead4731e73fd9055496732bcc0a4/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", size = 144800 }, + { url = "https://files.pythonhosted.org/packages/9a/65/bdb9bc496d7d190d725e96816e20e2ae3a6fa42a5cac99c3c3d6ff884118/charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", size = 94836 }, + { url = "https://files.pythonhosted.org/packages/3e/67/7b72b69d25b89c0b3cea583ee372c43aa24df15f0e0f8d3982c57804984b/charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", size = 102187 }, + { url = "https://files.pythonhosted.org/packages/f3/89/68a4c86f1a0002810a27f12e9a7b22feb198c59b2f05231349fbce5c06f4/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", size = 194617 }, + { url = "https://files.pythonhosted.org/packages/4f/cd/8947fe425e2ab0aa57aceb7807af13a0e4162cd21eee42ef5b053447edf5/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", size = 125310 }, + { url = "https://files.pythonhosted.org/packages/5b/f0/b5263e8668a4ee9becc2b451ed909e9c27058337fda5b8c49588183c267a/charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", size = 119126 }, + { url = "https://files.pythonhosted.org/packages/ff/6e/e445afe4f7fda27a533f3234b627b3e515a1b9429bc981c9a5e2aa5d97b6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", size = 139342 }, + { url = "https://files.pythonhosted.org/packages/a1/b2/4af9993b532d93270538ad4926c8e37dc29f2111c36f9c629840c57cd9b3/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", size = 149383 }, + { url = "https://files.pythonhosted.org/packages/fb/6f/4e78c3b97686b871db9be6f31d64e9264e889f8c9d7ab33c771f847f79b7/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", size = 142214 }, + { url = "https://files.pythonhosted.org/packages/2b/c9/1c8fe3ce05d30c87eff498592c89015b19fade13df42850aafae09e94f35/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", size = 144104 }, + { url = "https://files.pythonhosted.org/packages/ee/68/efad5dcb306bf37db7db338338e7bb8ebd8cf38ee5bbd5ceaaaa46f257e6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", size = 146255 }, + { url = "https://files.pythonhosted.org/packages/0c/75/1ed813c3ffd200b1f3e71121c95da3f79e6d2a96120163443b3ad1057505/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", size = 140251 }, + { url = "https://files.pythonhosted.org/packages/7d/0d/6f32255c1979653b448d3c709583557a4d24ff97ac4f3a5be156b2e6a210/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", size = 148474 }, + { url = "https://files.pythonhosted.org/packages/ac/a0/c1b5298de4670d997101fef95b97ac440e8c8d8b4efa5a4d1ef44af82f0d/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", size = 151849 }, + { url = "https://files.pythonhosted.org/packages/04/4f/b3961ba0c664989ba63e30595a3ed0875d6790ff26671e2aae2fdc28a399/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", size = 149781 }, + { url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 }, + { url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 }, + { url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 }, + { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, ] [[package]] @@ -39,6 +168,68 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, +] + +[[package]] +name = "dom-toml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "domdf-python-tools" }, + { name = "tomli" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/34/f7690cf288eaa86b55c8f1b890d0834e6df44a026a88eca12274fcd624ab/dom_toml-2.0.0.tar.gz", hash = "sha256:3c07e8436538994974127b1ae037661d1a779ac915c44fd06b3ab5fe140ff589", size = 11133 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/99/b6fc87dff3138491d81676bdcbf1531080925ba41486ec1dafd86e33fdbc/dom_toml-2.0.0-py3-none-any.whl", hash = "sha256:0b6d02a72bcbc6be8175c61afc30623bbb6b74c4650f2a806fbc3fb7fe86935d", size = 13376 }, +] + +[[package]] +name = "domdf-python-tools" +version = "3.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "natsort" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6b/78/974e10c583ba9d2302e748c9585313a7f2c7ba00e4f600324f432e38fe68/domdf_python_tools-3.9.0.tar.gz", hash = "sha256:1f8a96971178333a55e083e35610d7688cd7620ad2b99790164e1fc1a3614c18", size = 103792 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/e9/7447a88b217650a74927d3444a89507986479a69b83741900eddd34167fe/domdf_python_tools-3.9.0-py3-none-any.whl", hash = "sha256:4e1ef365cbc24627d6d1e90cf7d46d8ab8df967e1237f4a26885f6986c78872e", size = 127106 }, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 }, +] + [[package]] name = "iniconfig" version = "2.0.0" @@ -48,16 +239,125 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, ] +[[package]] +name = "jinja2" +version = "3.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + [[package]] name = "mypy" version = "1.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e8/21/7e9e523537991d145ab8a0a2fd98548d67646dc2aaaf6091c31ad883e7c1/mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", size = 3152532 } wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/8c/206de95a27722b5b5a8c85ba3100467bd86299d92a4f71c6b9aa448bfa2f/mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a", size = 11020731 }, + { url = "https://files.pythonhosted.org/packages/ab/bb/b31695a29eea76b1569fd28b4ab141a1adc9842edde080d1e8e1776862c7/mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80", size = 10184276 }, + { url = "https://files.pythonhosted.org/packages/a5/2d/4a23849729bb27934a0e079c9c1aad912167d875c7b070382a408d459651/mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7", size = 12587706 }, + { url = "https://files.pythonhosted.org/packages/5c/c3/d318e38ada50255e22e23353a469c791379825240e71b0ad03e76ca07ae6/mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f", size = 13105586 }, + { url = "https://files.pythonhosted.org/packages/4a/25/3918bc64952370c3dbdbd8c82c363804678127815febd2925b7273d9482c/mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372", size = 9632318 }, { url = "https://files.pythonhosted.org/packages/d0/19/de0822609e5b93d02579075248c7aa6ceaddcea92f00bf4ea8e4c22e3598/mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d", size = 10939027 }, { url = "https://files.pythonhosted.org/packages/c8/71/6950fcc6ca84179137e4cbf7cf41e6b68b4a339a1f5d3e954f8c34e02d66/mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d", size = 10108699 }, { url = "https://files.pythonhosted.org/packages/26/50/29d3e7dd166e74dc13d46050b23f7d6d7533acf48f5217663a3719db024e/mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b", size = 12506263 }, @@ -85,12 +385,48 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, ] +[[package]] +name = "myst-parser" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "jinja2" }, + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "pyyaml" }, + { name = "sphinx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/55/6d1741a1780e5e65038b74bce6689da15f620261c490c3511eb4c12bac4b/myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531", size = 93858 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/b4/b036f8fdb667587bb37df29dc6644681dd78b7a2a6321a34684b79412b28/myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d", size = 84563 }, +] + +[[package]] +name = "natsort" +version = "8.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268 }, +] + [[package]] name = "numpy" version = "2.1.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/25/ca/1166b75c21abd1da445b97bf1fa2f14f423c6cfb4fc7c4ef31dccf9f6a94/numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761", size = 20166090 } wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/80/d572a4737626372915bca41c3afbfec9d173561a39a0a61bacbbfd1dafd4/numpy-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff", size = 21152472 }, + { url = "https://files.pythonhosted.org/packages/6f/bb/7bfba10c791ae3bb6716da77ad85a82d5fac07fc96fb0023ef0571df9d20/numpy-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5", size = 13747967 }, + { url = "https://files.pythonhosted.org/packages/da/d6/2df7bde35f0478455f0be5934877b3e5a505f587b00230f54a519a6b55a5/numpy-2.1.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1", size = 5354921 }, + { url = "https://files.pythonhosted.org/packages/d1/bb/75b945874f931494891eac6ca06a1764d0e8208791f3addadb2963b83527/numpy-2.1.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd", size = 6888603 }, + { url = "https://files.pythonhosted.org/packages/68/a7/fde73636f6498dbfa6d82fc336164635fe592f1ad0d13285fcb6267fdc1c/numpy-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3", size = 13889862 }, + { url = "https://files.pythonhosted.org/packages/05/db/5d9c91b2e1e2e72be1369278f696356d44975befcae830daf2e667dcb54f/numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098", size = 16328151 }, + { url = "https://files.pythonhosted.org/packages/3e/6a/7eb732109b53ae64a29e25d7e68eb9d6611037f6354875497008a49e74d3/numpy-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c", size = 16704107 }, + { url = "https://files.pythonhosted.org/packages/88/cc/278113b66a1141053cbda6f80e4200c6da06b3079c2d27bda1fde41f2c1f/numpy-2.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4", size = 14385789 }, + { url = "https://files.pythonhosted.org/packages/f5/69/eb20f5e1bfa07449bc67574d2f0f7c1e6b335fb41672e43861a7727d85f2/numpy-2.1.3-cp310-cp310-win32.whl", hash = "sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23", size = 6536706 }, + { url = "https://files.pythonhosted.org/packages/8e/8b/1c131ab5a94c1086c289c6e1da1d843de9dbd95fe5f5ee6e61904c9518e2/numpy-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0", size = 12864165 }, { url = "https://files.pythonhosted.org/packages/ad/81/c8167192eba5247593cd9d305ac236847c2912ff39e11402e72ae28a4985/numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d", size = 21156252 }, { url = "https://files.pythonhosted.org/packages/da/74/5a60003fc3d8a718d830b08b654d0eea2d2db0806bab8f3c2aca7e18e010/numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41", size = 13784119 }, { url = "https://files.pythonhosted.org/packages/47/7c/864cb966b96fce5e63fcf25e1e4d957fe5725a635e5f11fe03f39dd9d6b5/numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9", size = 5352978 }, @@ -131,6 +467,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/62/1d3204313357591c913c32132a28f09a26357e33ea3c4e2fe81269e0dca1/numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17", size = 14067180 }, { url = "https://files.pythonhosted.org/packages/24/d7/78a40ed1d80e23a774cb8a34ae8a9493ba1b4271dde96e56ccdbab1620ef/numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48", size = 6291907 }, { url = "https://files.pythonhosted.org/packages/86/09/a5ab407bd7f5f5599e6a9261f964ace03a73e7c6928de906981c31c38082/numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4", size = 12644098 }, + { url = "https://files.pythonhosted.org/packages/00/e7/8d8bb791b62586cc432ecbb70632b4f23b7b7c88df41878de7528264f6d7/numpy-2.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f", size = 20983893 }, + { url = "https://files.pythonhosted.org/packages/5e/f3/cb8118a044b5007586245a650360c9f5915b2f4232dd7658bb7a63dd1d02/numpy-2.1.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4", size = 6752501 }, + { url = "https://files.pythonhosted.org/packages/53/f5/365b46439b518d2ec6ebb880cc0edf90f225145dfd4db7958334f7164530/numpy-2.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d", size = 16142601 }, + { url = "https://files.pythonhosted.org/packages/03/c2/d1fee6ba999aa7cd41ca6856937f2baaf604c3eec1565eae63451ec31e5e/numpy-2.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb", size = 12771397 }, ] [[package]] @@ -154,6 +494,13 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/70/c853aec59839bceed032d52010ff5f1b8d87dc3114b762e4ba2727661a3b/pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", size = 12580827 }, + { url = "https://files.pythonhosted.org/packages/99/f2/c4527768739ffa4469b2b4fff05aa3768a478aed89a2f271a79a40eee984/pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", size = 11303897 }, + { url = "https://files.pythonhosted.org/packages/ed/12/86c1747ea27989d7a4064f806ce2bae2c6d575b950be087837bdfcabacc9/pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", size = 66480908 }, + { url = "https://files.pythonhosted.org/packages/44/50/7db2cd5e6373ae796f0ddad3675268c8d59fb6076e66f0c339d61cea886b/pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", size = 13064210 }, + { url = "https://files.pythonhosted.org/packages/61/61/a89015a6d5536cb0d6c3ba02cebed51a95538cf83472975275e28ebf7d0c/pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", size = 16754292 }, + { url = "https://files.pythonhosted.org/packages/ce/0d/4cc7b69ce37fac07645a94e1d4b0880b15999494372c1523508511b09e40/pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", size = 14416379 }, + { url = "https://files.pythonhosted.org/packages/31/9e/6ebb433de864a6cd45716af52a4d7a8c3c9aaf3a98368e61db9e69e69a9c/pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", size = 11598471 }, { url = "https://files.pythonhosted.org/packages/a8/44/d9502bf0ed197ba9bf1103c9867d5904ddcaf869e52329787fc54ed70cc8/pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", size = 12602222 }, { url = "https://files.pythonhosted.org/packages/52/11/9eac327a38834f162b8250aab32a6781339c69afe7574368fffe46387edf/pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", size = 11321274 }, { url = "https://files.pythonhosted.org/packages/45/fb/c4beeb084718598ba19aa9f5abbc8aed8b42f90930da861fcb1acdb54c3a/pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", size = 15579836 }, @@ -192,15 +539,44 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, ] +[[package]] +name = "pydata-sphinx-theme" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accessible-pygments" }, + { name = "babel" }, + { name = "beautifulsoup4" }, + { name = "docutils" }, + { name = "pygments" }, + { name = "sphinx" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/c3/5240f2a5dc0b4856655c003466f70aa50d676b1709e5b04f0bee296bbd28/pydata_sphinx_theme-0.16.0.tar.gz", hash = "sha256:721dd26e05fa8b992d66ef545536e6cbe0110afb9865820a08894af1ad6f7707", size = 2407197 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/92/38f384061e1361fac7092c35e932c0e08026fb9080bf3fbf05f4c3bb6bda/pydata_sphinx_theme-0.16.0-py3-none-any.whl", hash = "sha256:18c810ee4e67e05281e371e156c1fb5bb0fa1f2747240461b225272f7d8d57d8", size = 6739948 }, +] + +[[package]] +name = "pygments" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, +] + [[package]] name = "pytest" version = "8.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } wheels = [ @@ -228,6 +604,65 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, ] +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + [[package]] name = "ruff" version = "0.7.4" @@ -262,6 +697,128 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, ] +[[package]] +name = "snowballstemmer" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 }, +] + +[[package]] +name = "soupsieve" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 }, +] + +[[package]] +name = "sphinx" +version = "8.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 }, +] + +[[package]] +name = "sphinx-pyproject" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dom-toml" }, + { name = "domdf-python-tools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/97/aa8cec3da3e78f2c396b63332e2fe92fe43f7ff2ad19b3998735f28b0a7f/sphinx_pyproject-0.3.0.tar.gz", hash = "sha256:efc4ee9d96f579c4e4ed1ac273868c64565e88c8e37fe6ec2dc59fbcd57684ab", size = 7695 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/d5/89cb47c6399fd57ca451af15361499813c5d53e588cb6e00d89411ce724f/sphinx_pyproject-0.3.0-py3-none-any.whl", hash = "sha256:3aca968919f5ecd390f96874c3f64a43c9c7fcfdc2fd4191a781ad9228501b52", size = 23076 }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, +] + +[[package]] +name = "tomli" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/e4/1b6cbcc82d8832dd0ce34767d5c560df8a3547ad8cbc427f34601415930a/tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8", size = 16622 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/f7/4da0ffe1892122c9ea096c57f64c2753ae5dd3ce85488802d11b0992cc6d/tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391", size = 13750 }, +] + [[package]] name = "typing-extensions" version = "4.12.2" @@ -279,3 +836,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/e1/34/943888654477a574a wheels = [ { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586 }, ] + +[[package]] +name = "urllib3" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, +] From 4513bcb07bee3ac610299cd235bd91b96a3a9583 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 16:27:26 -0600 Subject: [PATCH 12/59] Update actions/checkout versions across workflows --- .github/workflows/R-CMD-check.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/test-coverage.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 98fb135..5295f41 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -22,7 +22,7 @@ jobs: R_KEEP_PKG_SOURCE: yes steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup pandoc uses: r-lib/actions/setup-pandoc@v2 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 507fedb..2c8b81e 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup R uses: r-lib/actions/setup-r@v2 diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 2d30441..9f6749f 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup R uses: r-lib/actions/setup-r@v2 From 44fd662d34f58214cd4fe3ecdd0ed8c6292b2936 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 16:27:53 -0600 Subject: [PATCH 13/59] Add Python docs generation to docs workflow --- .github/workflows/{pkgdown.yaml => docs.yaml} | 34 ++++++++++++++++--- .github/workflows/pytest-coverage.yaml | 2 +- python/docs/source/index.rst | 3 ++ 3 files changed, 33 insertions(+), 6 deletions(-) rename .github/workflows/{pkgdown.yaml => docs.yaml} (61%) diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/docs.yaml similarity index 61% rename from .github/workflows/pkgdown.yaml rename to .github/workflows/docs.yaml index 5912d61..bc9bc6e 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/docs.yaml @@ -5,14 +5,18 @@ on: push: branches: [main, master] -name: pkgdown +name: docs + +env: + PYTHONUNBUFFERED: "1" + UV_SYSTEM_PYTHON: 1 jobs: build-pkgdown-site: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup pandoc uses: r-lib/actions/setup-pandoc@v2 @@ -28,15 +32,35 @@ jobs: extra-packages: any::pkgdown, local::. needs: website - - name: Build pkgdown site + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: python/pyproject.toml + cache-suffix: docs + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version-file: python/pyproject.toml + + - name: Install Python dependencies + working-directory: python + shell: bash + run: uv pip install .[docs] + + - name: Build R docs run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) shell: Rscript {0} + - name: Build Python docs + run: sphinx-build python/docs/source docs/python + - name: Configure pages - uses: actions/configure-pages@v3 + uses: actions/configure-pages@v5 - name: Upload artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: path: 'docs' diff --git a/.github/workflows/pytest-coverage.yaml b/.github/workflows/pytest-coverage.yaml index 183d0ec..416d3ef 100644 --- a/.github/workflows/pytest-coverage.yaml +++ b/.github/workflows/pytest-coverage.yaml @@ -35,7 +35,7 @@ jobs: - name: Install Python dependencies working-directory: python shell: bash - run: uv pip install .[dev] + run: uv pip install .[test] - name: Run pytest run: | diff --git a/python/docs/source/index.rst b/python/docs/source/index.rst index ffba847..8ae9bb3 100644 --- a/python/docs/source/index.rst +++ b/python/docs/source/index.rst @@ -22,6 +22,9 @@ used throughout CCAO applications, models, and diagnostics. For generalized versions of assessment-related functions, see `assesspy `_. +For detailed documentation on included functions and data, `visit the full reference +list `_. + Installation ------------ From 4070f6f1972382eea06ab3ac3b334f030b5c49ab Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 16:32:34 -0600 Subject: [PATCH 14/59] Fix ruff linter errors --- python/ccao/__init__.py | 6 +----- python/ccao/vars_funs.py | 3 ++- python/tests/test_vars_funs.py | 25 +++++++++++++++++++------ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/python/ccao/__init__.py b/python/ccao/__init__.py index 62100ed..f203ad4 100644 --- a/python/ccao/__init__.py +++ b/python/ccao/__init__.py @@ -1,5 +1 @@ -# Use explicit re-exports to appease flake8 -from ccao.vars_funs import ( - vars_dict as vars_dict, - vars_rename as vars_rename, -) +from ccao.vars_funs import vars_dict, vars_rename diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index 1614470..735d39c 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -109,7 +109,8 @@ def vars_rename( # Get a list of possible names_from and names_to from dictionary possible_names_args = [ - col.replace(f"{var_name_prefix}_", "") for col in dictionary_var_columns + col.replace(f"{var_name_prefix}_", "") + for col in dictionary_var_columns ] # Validate names arguments diff --git a/python/tests/test_vars_funs.py b/python/tests/test_vars_funs.py index 776cc50..4b78c0f 100644 --- a/python/tests/test_vars_funs.py +++ b/python/tests/test_vars_funs.py @@ -9,7 +9,10 @@ def test_vars_rename_input_data_is_dataframe(output_type, chars_sample_athena): data = chars_sample_athena.iloc[:, 13:19].copy() result = ccao.vars_rename( - data=data, names_from="athena", names_to="pretty", output_type=output_type + data=data, + names_from="athena", + names_to="pretty", + output_type=output_type, ) expected = [ "Apartments", @@ -56,8 +59,12 @@ def test_vars_rename_unmatched_cols_unchanged(): assert result == unmatched_colnames -@pytest.mark.parametrize("output_type", ["vector", ccao.vars_funs.OutputType.VECTOR]) -def test_vars_rename_output_type_enum_or_string(output_type, chars_sample_athena): +@pytest.mark.parametrize( + "output_type", ["vector", ccao.vars_funs.OutputType.VECTOR] +) +def test_vars_rename_output_type_enum_or_string( + output_type, chars_sample_athena +): # Both the enum and string versions of output_type should work result = ccao.vars_rename( data=chars_sample_athena.iloc[:, 13:19], @@ -133,14 +140,20 @@ def test_vars_rename_invalid_dictionary_missing_variable_columns(): @pytest.mark.parametrize("names_from,names_to", [(1, "pretty"), ("pretty", 1)]) def test_vars_rename_invalid_names_type(names_from, names_to): with pytest.raises(ValueError) as exc: - ccao.vars_rename(data=["1", "2", "3"], names_from=names_from, names_to=names_to) + ccao.vars_rename( + data=["1", "2", "3"], names_from=names_from, names_to=names_to + ) assert "must be strings" in str(exc.value) -@pytest.mark.parametrize("names_from,names_to", [("1", "pretty"), ("pretty", "1")]) +@pytest.mark.parametrize( + "names_from,names_to", [("1", "pretty"), ("pretty", "1")] +) def test_vars_rename_invalid_names_value(names_from, names_to): with pytest.raises(ValueError) as exc: - ccao.vars_rename(data=["1", "2", "3"], names_from=names_from, names_to=names_to) + ccao.vars_rename( + data=["1", "2", "3"], names_from=names_from, names_to=names_to + ) assert "must be one of" in str(exc.value) From 854aefc5962f3853d1b7f6ece68f28f88fdb52e8 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 16:34:12 -0600 Subject: [PATCH 15/59] Install both test and docs requirements when running pytest --- .github/workflows/pytest-coverage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pytest-coverage.yaml b/.github/workflows/pytest-coverage.yaml index 416d3ef..74da761 100644 --- a/.github/workflows/pytest-coverage.yaml +++ b/.github/workflows/pytest-coverage.yaml @@ -35,7 +35,7 @@ jobs: - name: Install Python dependencies working-directory: python shell: bash - run: uv pip install .[test] + run: uv pip install .[test,docs] - name: Run pytest run: | From 8afc1d45a4b631897e833f879dfbadc4ab0a32b0 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 16:37:10 -0600 Subject: [PATCH 16/59] Fix paths in pytest-coverage workflow --- .github/workflows/pytest-coverage.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pytest-coverage.yaml b/.github/workflows/pytest-coverage.yaml index 74da761..dcefc47 100644 --- a/.github/workflows/pytest-coverage.yaml +++ b/.github/workflows/pytest-coverage.yaml @@ -38,6 +38,8 @@ jobs: run: uv pip install .[test,docs] - name: Run pytest + working-directory: python + shell: bash run: | pytest --doctest-modules \ --junitxml=junit/test-results-${{ matrix.python-version }}.xmlpytest @@ -46,5 +48,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: pytest-results-${{ matrix.python-version }} - path: junit/test-results-${{ matrix.python-version }}.xml + name: python/pytest-results-${{ matrix.python-version }} + path: python/junit/test-results-${{ matrix.python-version }}.xml From 2549daf84b256252fca490287d9bc26dec7ea34e Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 16:47:08 -0600 Subject: [PATCH 17/59] Better path management in docs conf.py --- python/docs/source/conf.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python/docs/source/conf.py b/python/docs/source/conf.py index 60c1c59..ceabeaf 100644 --- a/python/docs/source/conf.py +++ b/python/docs/source/conf.py @@ -1,13 +1,14 @@ -import os +import pathlib import sys from sphinx_pyproject import SphinxConfig # Add source path to sys path so that autodoc can load functions -sys.path.append(os.path.abspath("../..")) +rootdir = pathlib.Path(__file__).resolve().parent.parent.parent +sys.path.append(str(rootdir.resolve())) # Loads config from pyproject.toml -config = SphinxConfig("../../pyproject.toml", globalns=globals()) +config = SphinxConfig(rootdir / "pyproject.toml", globalns=globals()) # Options that can't be parsed by sphinx-pyproject html_sidebars = {"**": []} From 723834eb05930a239dc6266432ab5406b60f0deb Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 16:52:18 -0600 Subject: [PATCH 18/59] Rename build jobs in docs workflow --- .github/workflows/docs.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index bc9bc6e..94dc315 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -12,7 +12,7 @@ env: UV_SYSTEM_PYTHON: 1 jobs: - build-pkgdown-site: + build: runs-on: ubuntu-latest steps: - name: Checkout @@ -66,7 +66,7 @@ jobs: deploy: if: contains(fromJSON('["main", "master"]'), github.ref_name) && github.event_name != 'pull_request' - needs: build-pkgdown-site + needs: build runs-on: ubuntu-latest environment: name: github-pages From c323370ed1fe1dedf1555d2ab765669a532715e1 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 16:52:38 -0600 Subject: [PATCH 19/59] Include csv files in package data when building Python package --- python/pyproject.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/pyproject.toml b/python/pyproject.toml index 2fcb2dd..b6be416 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -25,6 +25,12 @@ docs = [ "sphinx-pyproject>=0.3.0" ] +[tool.setuptools] +include-package-data = true + +[tool.setuptools.package-data] +"*" = ["*.csv"] + [tool.ruff] line-length = 79 From 9cc256b260a001bb71cb6df836bb1c683f9d7a12 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 16:57:53 -0600 Subject: [PATCH 20/59] Temporarily disable branch restriction for docs deployment to test it out --- .github/workflows/docs.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 94dc315..f064ebd 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -65,7 +65,6 @@ jobs: path: 'docs' deploy: - if: contains(fromJSON('["main", "master"]'), github.ref_name) && github.event_name != 'pull_request' needs: build runs-on: ubuntu-latest environment: From eb9a6192162c99d8eb868def20bf8955d20f19ff Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 17:13:32 -0600 Subject: [PATCH 21/59] Update deploy-pages version --- .github/workflows/docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index f064ebd..3cef368 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -76,4 +76,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v2 + uses: actions/deploy-pages@v4 From bbbaf68f77c370669c1e88d63b2043a62a06cf21 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 17:16:57 -0600 Subject: [PATCH 22/59] Revert "Temporarily disable branch restriction for docs deployment to test it out" This reverts commit 9cc256b260a001bb71cb6df836bb1c683f9d7a12. --- .github/workflows/docs.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 3cef368..d1b8bfd 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -65,6 +65,7 @@ jobs: path: 'docs' deploy: + if: contains(fromJSON('["main", "master"]'), github.ref_name) && github.event_name != 'pull_request' needs: build runs-on: ubuntu-latest environment: From b0055b4ee79d3b9291be5d15ee64228382ef66fd Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 17:19:16 -0600 Subject: [PATCH 23/59] Fix broken link in Python docs --- python/docs/source/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/docs/source/index.rst b/python/docs/source/index.rst index 8ae9bb3..07fbf46 100644 --- a/python/docs/source/index.rst +++ b/python/docs/source/index.rst @@ -23,7 +23,7 @@ versions of assessment-related functions, see `assesspy `_. For detailed documentation on included functions and data, `visit the full reference -list `_. +list `_. Installation From be12f3ebd71f90ab5bf6ed311991625de5820e43 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 17:21:05 -0600 Subject: [PATCH 24/59] Switch to new style python type hints since we don't support 3.9 anyway --- python/ccao/vars_funs.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index 735d39c..a21f06c 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -1,7 +1,6 @@ # Functions for translating variables between different data sources import enum import importlib.resources -import typing import pandas as pd @@ -28,12 +27,12 @@ def values(cls) -> list[str]: def vars_rename( - data: typing.Union[list[str], pd.DataFrame], + data: list[str] | pd.DataFrame, names_from: str, names_to: str, - output_type: typing.Union[OutputType, str] = OutputType.INPLACE, - dictionary: typing.Union[pd.DataFrame, None] = None, -) -> typing.Union[list[str], pd.DataFrame]: + output_type: OutputType | str = OutputType.INPLACE, + dictionary: pd.DataFrame | None = None, +) -> list[str] | pd.DataFrame: """ Rename variables from one naming convention to another. From bd6835de80f49db090b4da10745176d81e650cd0 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 17:25:00 -0600 Subject: [PATCH 25/59] Remove unnecessary templates_path config from pyproject.toml --- python/pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/python/pyproject.toml b/python/pyproject.toml index b6be416..cf56c54 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -51,7 +51,6 @@ extensions = [ "sphinx.ext.doctest", "myst_parser" ] -# templates_path = ["_templates"] highlight_language = "none" html_theme = "pydata_sphinx_theme" html_logo = "../images/logo.png" From 5eb1e231d4737ead7349493e03e07de9a57c247e Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Thu, 21 Nov 2024 17:30:07 -0600 Subject: [PATCH 26/59] Empty commit to try to bust build-pkgdown-site actions cache From 1f7290ed6c6e2d88ef63f0c63dbd123d54f24c38 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Fri, 22 Nov 2024 23:44:00 +0000 Subject: [PATCH 27/59] Draft Python version of vars_recode --- python/ccao/__init__.py | 2 +- python/ccao/vars_funs.py | 170 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 162 insertions(+), 10 deletions(-) diff --git a/python/ccao/__init__.py b/python/ccao/__init__.py index f203ad4..8527c2d 100644 --- a/python/ccao/__init__.py +++ b/python/ccao/__init__.py @@ -1 +1 @@ -from ccao.vars_funs import vars_dict, vars_rename +from ccao.vars_funs import vars_dict, vars_recode, vars_rename diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index a21f06c..6bcfb3e 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -14,11 +14,8 @@ var_name_prefix = "var_name" -class OutputType(enum.Enum): - """Possible output types for variable renaming""" - - INPLACE = "inplace" - VECTOR = "vector" +class Enum(enum.Enum): + """Enum subclass with extra convenience functions""" @classmethod def values(cls) -> list[str]: @@ -26,6 +23,25 @@ def values(cls) -> list[str]: return [type_.value for type_ in cls] +class OutputType(Enum): + """Possible output types for variable renaming""" + + INPLACE = "inplace" + VECTOR = "vector" + + +def _validate_dictionary( + dictionary: pd.DataFrame | None, + default: pd.DataFrame +) -> pd.DataFrame: + """Helper function to validate `dictionary` arguments that are shared among + variable management functions.""" + # Validate the dictionary schema + dictionary = dictionary if dictionary is not None else vars_dict + if not isinstance(dictionary, pd.DataFrame) or len(dictionary) == 0: + raise ValueError("dictionary must be a non-empty pandas DataFrame") + + def vars_rename( data: list[str] | pd.DataFrame, names_from: str, @@ -89,10 +105,9 @@ def vars_rename( output_type="vector" ) """ - # Validate the dictionary schema - dictionary = dictionary if dictionary is not None else vars_dict - if not isinstance(dictionary, pd.DataFrame) or len(dictionary) == 0: - raise ValueError("dictionary must be a non-empty pandas DataFrame") + + # Validate input dictionary + dictionary = _validate_dictionary(dictionary, default=vars_dict) # Make sure the dictionary contains variable columns dictionary_var_columns = [ @@ -153,3 +168,140 @@ def vars_rename( return [mapping.get(col, col) for col in data] else: raise TypeError("data must be a DataFrame or list of column names") + + +class CodeType(Enum): + """Possible code formats for variable recoding""" + + LONG = "long" + SHORT = "short" + CODE = "code" + + +def vars_recode( + data: pd.DataFrame, + cols: list[str] | None = None, + code_type: CodeType | str = "long", + as_factor: bool = True, + dictionary: pd.DataFrame | None = None +) -> pd.DataFrame: + """ + Replace numerically coded variables with human-readable values. + + The system of record stores characteristic values in a numerically encoded + format. This function can be used to translate those values into a + human-readable format. For example, EXT_WALL = 2 will become + EXT_WALL = "Frame + Masonry". Note that the values and their translations + must be specified via a user-defined dictionary. The default dictionary is + :data:`vars_dict`. + + :param data: + A pandas DataFrame with columns to have values replaced. + :type data: pandas.DataFrame + + :param cols: + A list of column names to be transformed, or None to select all columns. + :type cols: list[str] + + :param code_type: The recoding type. Options are: + - "long", which transforms EXT_WALL = 1 to EXT_WALL = Frame + - "short", which transforms EXT_WALL = 1 to EXT_WALL = FRME + - "code", which keeps the original values (useful for removing + improperly coded values). + :type code_type: CodeType or str + + :param as_factor: + If True, re-encoded values will be returned as categorical variables + (pandas Categorical). + If False, re-encoded values will be returned as plain strings. + :type as_factor: bool + + :param dictionary: + A pandas DataFrame representing the dictionary used to translate + encodings. When None, defaults to :data:`vars_dict`. + :type dictionary: pandas.DataFrame + + :raises ValueError: + If the dictionary is missing required columns or if invalid input is + provided. + + :return: + The input DataFrame with re-encoded values for the specified columns. + :rtype: pandas.DataFrame + + :note: + Values which are in the data but are NOT in the dictionary will be converted to NaN. + + :example: + + .. code-block:: python + + import ccao + + sample_data = ccao.sample_athena + + # Defaults to `long` code type + ccao.vars_recode(data=sample_data) + + # Recode to `short` code type + ccao.vars_recode(data=sample_data, code_type="short") + + # Recode only specified columns + ccao.vars_recode(data=sample_data, cols="GAR1_SIZE") + """ + # Validate input dictionary + dictionary = _validate_dictionary(dictionary, default=vars_dict) + + required_columns = {"var_code", "var_value", "var_value_short"} + if not required_columns.issubset(dictionary.columns): + raise ValueError( + "Input dictionary must contain the following columns: " + f"{', '.join(required_columns)}" + ) + + # Validate code type and convert it to the enum + if not isinstance(code_type, (CodeType, str)): + raise ValueError("code_type must be a string or CodeType instance") + + if isinstance(code_type, str): + if code_type not in CodeType.values(): + raise ValueError( + f"code_type must be one of {CodeType.values()} " + f"(got {code_type})" + ) + code_type = CodeType(code_type) + + # Validate input data format + if not isinstance(data, pd.DataFrame): + raise ValueError("data must be a pandas.DataFrame") + + # Filter and reshape the dictionary for easier lookup + dict_long = dictionary[ + (dictionary["var_type"] == "char") & (dictionary["var_data_type"] == "categorical") + ] + dict_long = dict_long.melt( + id_vars=[col for col in dict.columns if col.startswith("var_name_")], + value_vars=["var_code", "var_value", "var_value_short"], + var_name="var_type", + value_name="value" + ) + dict_long = dict_long.drop_duplicates(subset=["var_code", "var_value", "var_value_short", "var_name"]) + + # Function to apply transformation on each column + def transform_column(col: pd.Series, var_name: str) -> pd.Series: + if var_name in dict_long["var_name"].values: + var_rows = dict_long[dict_long["var_name"] == var_name] + idx = col.map(dict_long.set_index("var_code")["value"]).index + if as_factor: + return pd.Categorical(col.map(var_rows["value"].get), categories=var_rows["value"]) + else: + return col.map(var_rows["value"].get) + return col + + # Recode specified columns or all columns + cols = cols or data.columns + for col in cols: + if col in data.select_dtypes(include=["object", "category"]).columns: + data[col] = transform_column(data[col], col) + + return data From 1b6bbef3602825529c44af7832398ef83faef959 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 25 Nov 2024 12:10:55 -0600 Subject: [PATCH 28/59] Remove unnecessary .python-version file --- python/.python-version | 1 - 1 file changed, 1 deletion(-) delete mode 100644 python/.python-version diff --git a/python/.python-version b/python/.python-version deleted file mode 100644 index 2c07333..0000000 --- a/python/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.11 From bca6864a5e3a0d245a01ee046f565f13119c6835 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 25 Nov 2024 12:14:39 -0600 Subject: [PATCH 29/59] Add pip install directions to README and index.rst for Python package --- python/README.md | 8 +++++++- python/docs/source/index.rst | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/python/README.md b/python/README.md index 95260e5..5cb92d7 100644 --- a/python/README.md +++ b/python/README.md @@ -9,7 +9,13 @@ versions of assessment-related functions, see ## Installation -You can install the released version of `ccao` directly from GitHub: +Install the latest release of `ccao` from PyPI: + +```bash +pip install ccao +``` + +You can also install the most recent code directly from GitHub: ```bash pip install "git+https://github.com/ccao-data/ccao.git#egg=ccao&subdirectory=python" diff --git a/python/docs/source/index.rst b/python/docs/source/index.rst index 07fbf46..1df6361 100644 --- a/python/docs/source/index.rst +++ b/python/docs/source/index.rst @@ -29,7 +29,13 @@ list `_. Installation ------------ -You can install the released version of ``ccao`` directly from GitHub: +Install the latest release of ``ccao`` from PyPI: + +.. code-block:: python + + pip install ccao + +You can also install the most recent code directly from GitHub: .. code-block:: python From 5960235bc709bfc050e5b1a00b9f6a73d6e2cb89 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 25 Nov 2024 12:22:05 -0600 Subject: [PATCH 30/59] Remove unnecessary uv.lock file --- python/.gitignore | 1 + python/uv.lock | 847 ---------------------------------------------- 2 files changed, 1 insertion(+), 847 deletions(-) delete mode 100644 python/uv.lock diff --git a/python/.gitignore b/python/.gitignore index bec2db9..c7bd47a 100644 --- a/python/.gitignore +++ b/python/.gitignore @@ -25,6 +25,7 @@ share/python-wheels/ .installed.cfg *.egg MANIFEST +uv.lock # Environments .env diff --git a/python/uv.lock b/python/uv.lock deleted file mode 100644 index 3323c03..0000000 --- a/python/uv.lock +++ /dev/null @@ -1,847 +0,0 @@ -version = 1 -requires-python = ">=3.10" -resolution-markers = [ - "python_full_version < '3.11'", - "python_full_version == '3.11.*'", - "python_full_version >= '3.12'", -] - -[[package]] -name = "accessible-pygments" -version = "0.0.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903 }, -] - -[[package]] -name = "alabaster" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, -] - -[[package]] -name = "babel" -version = "2.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, -] - -[[package]] -name = "beautifulsoup4" -version = "4.12.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "soupsieve" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 }, -] - -[[package]] -name = "ccao" -version = "1.3.0" -source = { virtual = "." } -dependencies = [ - { name = "pandas" }, -] - -[package.optional-dependencies] -docs = [ - { name = "myst-parser" }, - { name = "pydata-sphinx-theme" }, - { name = "sphinx" }, - { name = "sphinx-pyproject" }, -] -test = [ - { name = "mypy" }, - { name = "pytest" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "mypy", marker = "extra == 'test'", specifier = ">=1.13.0" }, - { name = "myst-parser", marker = "extra == 'docs'", specifier = ">=4.0.0" }, - { name = "pandas", specifier = ">=2.2.3" }, - { name = "pydata-sphinx-theme", marker = "extra == 'docs'", specifier = ">=0.16.0" }, - { name = "pytest", marker = "extra == 'test'", specifier = ">=8.3.3" }, - { name = "ruff", marker = "extra == 'test'", specifier = ">=0.7.4" }, - { name = "sphinx", marker = "extra == 'docs'", specifier = ">=8.1.3" }, - { name = "sphinx-pyproject", marker = "extra == 'docs'", specifier = ">=0.3.0" }, -] - -[[package]] -name = "certifi" -version = "2024.8.30" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", size = 106620 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/8b/825cc84cf13a28bfbcba7c416ec22bf85a9584971be15b21dd8300c65b7f/charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", size = 196363 }, - { url = "https://files.pythonhosted.org/packages/23/81/d7eef6a99e42c77f444fdd7bc894b0ceca6c3a95c51239e74a722039521c/charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", size = 125639 }, - { url = "https://files.pythonhosted.org/packages/21/67/b4564d81f48042f520c948abac7079356e94b30cb8ffb22e747532cf469d/charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", size = 120451 }, - { url = "https://files.pythonhosted.org/packages/c2/72/12a7f0943dd71fb5b4e7b55c41327ac0a1663046a868ee4d0d8e9c369b85/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", size = 140041 }, - { url = "https://files.pythonhosted.org/packages/67/56/fa28c2c3e31217c4c52158537a2cf5d98a6c1e89d31faf476c89391cd16b/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", size = 150333 }, - { url = "https://files.pythonhosted.org/packages/f9/d2/466a9be1f32d89eb1554cf84073a5ed9262047acee1ab39cbaefc19635d2/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", size = 142921 }, - { url = "https://files.pythonhosted.org/packages/f8/01/344ec40cf5d85c1da3c1f57566c59e0c9b56bcc5566c08804a95a6cc8257/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", size = 144785 }, - { url = "https://files.pythonhosted.org/packages/73/8b/2102692cb6d7e9f03b9a33a710e0164cadfce312872e3efc7cfe22ed26b4/charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", size = 146631 }, - { url = "https://files.pythonhosted.org/packages/d8/96/cc2c1b5d994119ce9f088a9a0c3ebd489d360a2eb058e2c8049f27092847/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", size = 140867 }, - { url = "https://files.pythonhosted.org/packages/c9/27/cde291783715b8ec30a61c810d0120411844bc4c23b50189b81188b273db/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", size = 149273 }, - { url = "https://files.pythonhosted.org/packages/3a/a4/8633b0fc1a2d1834d5393dafecce4a1cc56727bfd82b4dc18fc92f0d3cc3/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", size = 152437 }, - { url = "https://files.pythonhosted.org/packages/64/ea/69af161062166b5975ccbb0961fd2384853190c70786f288684490913bf5/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", size = 150087 }, - { url = "https://files.pythonhosted.org/packages/3b/fd/e60a9d9fd967f4ad5a92810138192f825d77b4fa2a557990fd575a47695b/charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", size = 145142 }, - { url = "https://files.pythonhosted.org/packages/6d/02/8cb0988a1e49ac9ce2eed1e07b77ff118f2923e9ebd0ede41ba85f2dcb04/charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", size = 94701 }, - { url = "https://files.pythonhosted.org/packages/d6/20/f1d4670a8a723c46be695dff449d86d6092916f9e99c53051954ee33a1bc/charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", size = 102191 }, - { url = "https://files.pythonhosted.org/packages/9c/61/73589dcc7a719582bf56aae309b6103d2762b526bffe189d635a7fcfd998/charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", size = 193339 }, - { url = "https://files.pythonhosted.org/packages/77/d5/8c982d58144de49f59571f940e329ad6e8615e1e82ef84584c5eeb5e1d72/charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", size = 124366 }, - { url = "https://files.pythonhosted.org/packages/bf/19/411a64f01ee971bed3231111b69eb56f9331a769072de479eae7de52296d/charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", size = 118874 }, - { url = "https://files.pythonhosted.org/packages/4c/92/97509850f0d00e9f14a46bc751daabd0ad7765cff29cdfb66c68b6dad57f/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", size = 138243 }, - { url = "https://files.pythonhosted.org/packages/e2/29/d227805bff72ed6d6cb1ce08eec707f7cfbd9868044893617eb331f16295/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", size = 148676 }, - { url = "https://files.pythonhosted.org/packages/13/bc/87c2c9f2c144bedfa62f894c3007cd4530ba4b5351acb10dc786428a50f0/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", size = 141289 }, - { url = "https://files.pythonhosted.org/packages/eb/5b/6f10bad0f6461fa272bfbbdf5d0023b5fb9bc6217c92bf068fa5a99820f5/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", size = 142585 }, - { url = "https://files.pythonhosted.org/packages/3b/a0/a68980ab8a1f45a36d9745d35049c1af57d27255eff8c907e3add84cf68f/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", size = 144408 }, - { url = "https://files.pythonhosted.org/packages/d7/a1/493919799446464ed0299c8eef3c3fad0daf1c3cd48bff9263c731b0d9e2/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", size = 139076 }, - { url = "https://files.pythonhosted.org/packages/fb/9d/9c13753a5a6e0db4a0a6edb1cef7aee39859177b64e1a1e748a6e3ba62c2/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", size = 146874 }, - { url = "https://files.pythonhosted.org/packages/75/d2/0ab54463d3410709c09266dfb416d032a08f97fd7d60e94b8c6ef54ae14b/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", size = 150871 }, - { url = "https://files.pythonhosted.org/packages/8d/c9/27e41d481557be53d51e60750b85aa40eaf52b841946b3cdeff363105737/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", size = 148546 }, - { url = "https://files.pythonhosted.org/packages/ee/44/4f62042ca8cdc0cabf87c0fc00ae27cd8b53ab68be3605ba6d071f742ad3/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", size = 143048 }, - { url = "https://files.pythonhosted.org/packages/01/f8/38842422988b795220eb8038745d27a675ce066e2ada79516c118f291f07/charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", size = 94389 }, - { url = "https://files.pythonhosted.org/packages/0b/6e/b13bd47fa9023b3699e94abf565b5a2f0b0be6e9ddac9812182596ee62e4/charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", size = 101752 }, - { url = "https://files.pythonhosted.org/packages/d3/0b/4b7a70987abf9b8196845806198975b6aab4ce016632f817ad758a5aa056/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", size = 194445 }, - { url = "https://files.pythonhosted.org/packages/50/89/354cc56cf4dd2449715bc9a0f54f3aef3dc700d2d62d1fa5bbea53b13426/charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", size = 125275 }, - { url = "https://files.pythonhosted.org/packages/fa/44/b730e2a2580110ced837ac083d8ad222343c96bb6b66e9e4e706e4d0b6df/charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", size = 119020 }, - { url = "https://files.pythonhosted.org/packages/9d/e4/9263b8240ed9472a2ae7ddc3e516e71ef46617fe40eaa51221ccd4ad9a27/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", size = 139128 }, - { url = "https://files.pythonhosted.org/packages/6b/e3/9f73e779315a54334240353eaea75854a9a690f3f580e4bd85d977cb2204/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", size = 149277 }, - { url = "https://files.pythonhosted.org/packages/1a/cf/f1f50c2f295312edb8a548d3fa56a5c923b146cd3f24114d5adb7e7be558/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", size = 142174 }, - { url = "https://files.pythonhosted.org/packages/16/92/92a76dc2ff3a12e69ba94e7e05168d37d0345fa08c87e1fe24d0c2a42223/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", size = 143838 }, - { url = "https://files.pythonhosted.org/packages/a4/01/2117ff2b1dfc61695daf2babe4a874bca328489afa85952440b59819e9d7/charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", size = 146149 }, - { url = "https://files.pythonhosted.org/packages/f6/9b/93a332b8d25b347f6839ca0a61b7f0287b0930216994e8bf67a75d050255/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", size = 140043 }, - { url = "https://files.pythonhosted.org/packages/ab/f6/7ac4a01adcdecbc7a7587767c776d53d369b8b971382b91211489535acf0/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", size = 148229 }, - { url = "https://files.pythonhosted.org/packages/9d/be/5708ad18161dee7dc6a0f7e6cf3a88ea6279c3e8484844c0590e50e803ef/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", size = 151556 }, - { url = "https://files.pythonhosted.org/packages/5a/bb/3d8bc22bacb9eb89785e83e6723f9888265f3a0de3b9ce724d66bd49884e/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", size = 149772 }, - { url = "https://files.pythonhosted.org/packages/f7/fa/d3fc622de05a86f30beea5fc4e9ac46aead4731e73fd9055496732bcc0a4/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", size = 144800 }, - { url = "https://files.pythonhosted.org/packages/9a/65/bdb9bc496d7d190d725e96816e20e2ae3a6fa42a5cac99c3c3d6ff884118/charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", size = 94836 }, - { url = "https://files.pythonhosted.org/packages/3e/67/7b72b69d25b89c0b3cea583ee372c43aa24df15f0e0f8d3982c57804984b/charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", size = 102187 }, - { url = "https://files.pythonhosted.org/packages/f3/89/68a4c86f1a0002810a27f12e9a7b22feb198c59b2f05231349fbce5c06f4/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", size = 194617 }, - { url = "https://files.pythonhosted.org/packages/4f/cd/8947fe425e2ab0aa57aceb7807af13a0e4162cd21eee42ef5b053447edf5/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", size = 125310 }, - { url = "https://files.pythonhosted.org/packages/5b/f0/b5263e8668a4ee9becc2b451ed909e9c27058337fda5b8c49588183c267a/charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", size = 119126 }, - { url = "https://files.pythonhosted.org/packages/ff/6e/e445afe4f7fda27a533f3234b627b3e515a1b9429bc981c9a5e2aa5d97b6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", size = 139342 }, - { url = "https://files.pythonhosted.org/packages/a1/b2/4af9993b532d93270538ad4926c8e37dc29f2111c36f9c629840c57cd9b3/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", size = 149383 }, - { url = "https://files.pythonhosted.org/packages/fb/6f/4e78c3b97686b871db9be6f31d64e9264e889f8c9d7ab33c771f847f79b7/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", size = 142214 }, - { url = "https://files.pythonhosted.org/packages/2b/c9/1c8fe3ce05d30c87eff498592c89015b19fade13df42850aafae09e94f35/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", size = 144104 }, - { url = "https://files.pythonhosted.org/packages/ee/68/efad5dcb306bf37db7db338338e7bb8ebd8cf38ee5bbd5ceaaaa46f257e6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", size = 146255 }, - { url = "https://files.pythonhosted.org/packages/0c/75/1ed813c3ffd200b1f3e71121c95da3f79e6d2a96120163443b3ad1057505/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", size = 140251 }, - { url = "https://files.pythonhosted.org/packages/7d/0d/6f32255c1979653b448d3c709583557a4d24ff97ac4f3a5be156b2e6a210/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", size = 148474 }, - { url = "https://files.pythonhosted.org/packages/ac/a0/c1b5298de4670d997101fef95b97ac440e8c8d8b4efa5a4d1ef44af82f0d/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", size = 151849 }, - { url = "https://files.pythonhosted.org/packages/04/4f/b3961ba0c664989ba63e30595a3ed0875d6790ff26671e2aae2fdc28a399/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", size = 149781 }, - { url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 }, - { url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 }, - { url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 }, - { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, -] - -[[package]] -name = "docutils" -version = "0.21.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, -] - -[[package]] -name = "dom-toml" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "domdf-python-tools" }, - { name = "tomli" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/34/f7690cf288eaa86b55c8f1b890d0834e6df44a026a88eca12274fcd624ab/dom_toml-2.0.0.tar.gz", hash = "sha256:3c07e8436538994974127b1ae037661d1a779ac915c44fd06b3ab5fe140ff589", size = 11133 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/99/b6fc87dff3138491d81676bdcbf1531080925ba41486ec1dafd86e33fdbc/dom_toml-2.0.0-py3-none-any.whl", hash = "sha256:0b6d02a72bcbc6be8175c61afc30623bbb6b74c4650f2a806fbc3fb7fe86935d", size = 13376 }, -] - -[[package]] -name = "domdf-python-tools" -version = "3.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "natsort" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6b/78/974e10c583ba9d2302e748c9585313a7f2c7ba00e4f600324f432e38fe68/domdf_python_tools-3.9.0.tar.gz", hash = "sha256:1f8a96971178333a55e083e35610d7688cd7620ad2b99790164e1fc1a3614c18", size = 103792 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/e9/7447a88b217650a74927d3444a89507986479a69b83741900eddd34167fe/domdf_python_tools-3.9.0-py3-none-any.whl", hash = "sha256:4e1ef365cbc24627d6d1e90cf7d46d8ab8df967e1237f4a26885f6986c78872e", size = 127106 }, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, -] - -[[package]] -name = "idna" -version = "3.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, -] - -[[package]] -name = "imagesize" -version = "1.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 }, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, -] - -[[package]] -name = "jinja2" -version = "3.1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, -] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, -] - -[[package]] -name = "markupsafe" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, - { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, - { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, - { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, - { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, - { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, - { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, - { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, - { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, - { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, - { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, - { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, - { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, - { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, - { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, - { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, - { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, - { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, - { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, - { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, -] - -[[package]] -name = "mdit-py-plugins" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, -] - -[[package]] -name = "mypy" -version = "1.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e8/21/7e9e523537991d145ab8a0a2fd98548d67646dc2aaaf6091c31ad883e7c1/mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", size = 3152532 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/8c/206de95a27722b5b5a8c85ba3100467bd86299d92a4f71c6b9aa448bfa2f/mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a", size = 11020731 }, - { url = "https://files.pythonhosted.org/packages/ab/bb/b31695a29eea76b1569fd28b4ab141a1adc9842edde080d1e8e1776862c7/mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80", size = 10184276 }, - { url = "https://files.pythonhosted.org/packages/a5/2d/4a23849729bb27934a0e079c9c1aad912167d875c7b070382a408d459651/mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7", size = 12587706 }, - { url = "https://files.pythonhosted.org/packages/5c/c3/d318e38ada50255e22e23353a469c791379825240e71b0ad03e76ca07ae6/mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f", size = 13105586 }, - { url = "https://files.pythonhosted.org/packages/4a/25/3918bc64952370c3dbdbd8c82c363804678127815febd2925b7273d9482c/mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372", size = 9632318 }, - { url = "https://files.pythonhosted.org/packages/d0/19/de0822609e5b93d02579075248c7aa6ceaddcea92f00bf4ea8e4c22e3598/mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d", size = 10939027 }, - { url = "https://files.pythonhosted.org/packages/c8/71/6950fcc6ca84179137e4cbf7cf41e6b68b4a339a1f5d3e954f8c34e02d66/mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d", size = 10108699 }, - { url = "https://files.pythonhosted.org/packages/26/50/29d3e7dd166e74dc13d46050b23f7d6d7533acf48f5217663a3719db024e/mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b", size = 12506263 }, - { url = "https://files.pythonhosted.org/packages/3f/1d/676e76f07f7d5ddcd4227af3938a9c9640f293b7d8a44dd4ff41d4db25c1/mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73", size = 12984688 }, - { url = "https://files.pythonhosted.org/packages/9c/03/5a85a30ae5407b1d28fab51bd3e2103e52ad0918d1e68f02a7778669a307/mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca", size = 9626811 }, - { url = "https://files.pythonhosted.org/packages/fb/31/c526a7bd2e5c710ae47717c7a5f53f616db6d9097caf48ad650581e81748/mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5", size = 11077900 }, - { url = "https://files.pythonhosted.org/packages/83/67/b7419c6b503679d10bd26fc67529bc6a1f7a5f220bbb9f292dc10d33352f/mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e", size = 10074818 }, - { url = "https://files.pythonhosted.org/packages/ba/07/37d67048786ae84e6612575e173d713c9a05d0ae495dde1e68d972207d98/mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2", size = 12589275 }, - { url = "https://files.pythonhosted.org/packages/1f/17/b1018c6bb3e9f1ce3956722b3bf91bff86c1cefccca71cec05eae49d6d41/mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0", size = 13037783 }, - { url = "https://files.pythonhosted.org/packages/cb/32/cd540755579e54a88099aee0287086d996f5a24281a673f78a0e14dba150/mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2", size = 9726197 }, - { url = "https://files.pythonhosted.org/packages/11/bb/ab4cfdc562cad80418f077d8be9b4491ee4fb257440da951b85cbb0a639e/mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", size = 11069721 }, - { url = "https://files.pythonhosted.org/packages/59/3b/a393b1607cb749ea2c621def5ba8c58308ff05e30d9dbdc7c15028bca111/mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", size = 10063996 }, - { url = "https://files.pythonhosted.org/packages/d1/1f/6b76be289a5a521bb1caedc1f08e76ff17ab59061007f201a8a18cc514d1/mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8", size = 12584043 }, - { url = "https://files.pythonhosted.org/packages/a6/83/5a85c9a5976c6f96e3a5a7591aa28b4a6ca3a07e9e5ba0cec090c8b596d6/mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", size = 13036996 }, - { url = "https://files.pythonhosted.org/packages/b4/59/c39a6f752f1f893fccbcf1bdd2aca67c79c842402b5283563d006a67cf76/mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", size = 9737709 }, - { url = "https://files.pythonhosted.org/packages/3b/86/72ce7f57431d87a7ff17d442f521146a6585019eb8f4f31b7c02801f78ad/mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", size = 2647043 }, -] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, -] - -[[package]] -name = "myst-parser" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "jinja2" }, - { name = "markdown-it-py" }, - { name = "mdit-py-plugins" }, - { name = "pyyaml" }, - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/85/55/6d1741a1780e5e65038b74bce6689da15f620261c490c3511eb4c12bac4b/myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531", size = 93858 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/b4/b036f8fdb667587bb37df29dc6644681dd78b7a2a6321a34684b79412b28/myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d", size = 84563 }, -] - -[[package]] -name = "natsort" -version = "8.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268 }, -] - -[[package]] -name = "numpy" -version = "2.1.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/ca/1166b75c21abd1da445b97bf1fa2f14f423c6cfb4fc7c4ef31dccf9f6a94/numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761", size = 20166090 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/80/d572a4737626372915bca41c3afbfec9d173561a39a0a61bacbbfd1dafd4/numpy-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff", size = 21152472 }, - { url = "https://files.pythonhosted.org/packages/6f/bb/7bfba10c791ae3bb6716da77ad85a82d5fac07fc96fb0023ef0571df9d20/numpy-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5", size = 13747967 }, - { url = "https://files.pythonhosted.org/packages/da/d6/2df7bde35f0478455f0be5934877b3e5a505f587b00230f54a519a6b55a5/numpy-2.1.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1", size = 5354921 }, - { url = "https://files.pythonhosted.org/packages/d1/bb/75b945874f931494891eac6ca06a1764d0e8208791f3addadb2963b83527/numpy-2.1.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd", size = 6888603 }, - { url = "https://files.pythonhosted.org/packages/68/a7/fde73636f6498dbfa6d82fc336164635fe592f1ad0d13285fcb6267fdc1c/numpy-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3", size = 13889862 }, - { url = "https://files.pythonhosted.org/packages/05/db/5d9c91b2e1e2e72be1369278f696356d44975befcae830daf2e667dcb54f/numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098", size = 16328151 }, - { url = "https://files.pythonhosted.org/packages/3e/6a/7eb732109b53ae64a29e25d7e68eb9d6611037f6354875497008a49e74d3/numpy-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c", size = 16704107 }, - { url = "https://files.pythonhosted.org/packages/88/cc/278113b66a1141053cbda6f80e4200c6da06b3079c2d27bda1fde41f2c1f/numpy-2.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4", size = 14385789 }, - { url = "https://files.pythonhosted.org/packages/f5/69/eb20f5e1bfa07449bc67574d2f0f7c1e6b335fb41672e43861a7727d85f2/numpy-2.1.3-cp310-cp310-win32.whl", hash = "sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23", size = 6536706 }, - { url = "https://files.pythonhosted.org/packages/8e/8b/1c131ab5a94c1086c289c6e1da1d843de9dbd95fe5f5ee6e61904c9518e2/numpy-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0", size = 12864165 }, - { url = "https://files.pythonhosted.org/packages/ad/81/c8167192eba5247593cd9d305ac236847c2912ff39e11402e72ae28a4985/numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d", size = 21156252 }, - { url = "https://files.pythonhosted.org/packages/da/74/5a60003fc3d8a718d830b08b654d0eea2d2db0806bab8f3c2aca7e18e010/numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41", size = 13784119 }, - { url = "https://files.pythonhosted.org/packages/47/7c/864cb966b96fce5e63fcf25e1e4d957fe5725a635e5f11fe03f39dd9d6b5/numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9", size = 5352978 }, - { url = "https://files.pythonhosted.org/packages/09/ac/61d07930a4993dd9691a6432de16d93bbe6aa4b1c12a5e573d468eefc1ca/numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09", size = 6892570 }, - { url = "https://files.pythonhosted.org/packages/27/2f/21b94664f23af2bb52030653697c685022119e0dc93d6097c3cb45bce5f9/numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a", size = 13896715 }, - { url = "https://files.pythonhosted.org/packages/7a/f0/80811e836484262b236c684a75dfc4ba0424bc670e765afaa911468d9f39/numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b", size = 16339644 }, - { url = "https://files.pythonhosted.org/packages/fa/81/ce213159a1ed8eb7d88a2a6ef4fbdb9e4ffd0c76b866c350eb4e3c37e640/numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee", size = 16712217 }, - { url = "https://files.pythonhosted.org/packages/7d/84/4de0b87d5a72f45556b2a8ee9fc8801e8518ec867fc68260c1f5dcb3903f/numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0", size = 14399053 }, - { url = "https://files.pythonhosted.org/packages/7e/1c/e5fabb9ad849f9d798b44458fd12a318d27592d4bc1448e269dec070ff04/numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9", size = 6534741 }, - { url = "https://files.pythonhosted.org/packages/1e/48/a9a4b538e28f854bfb62e1dea3c8fea12e90216a276c7777ae5345ff29a7/numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2", size = 12869487 }, - { url = "https://files.pythonhosted.org/packages/8a/f0/385eb9970309643cbca4fc6eebc8bb16e560de129c91258dfaa18498da8b/numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e", size = 20849658 }, - { url = "https://files.pythonhosted.org/packages/54/4a/765b4607f0fecbb239638d610d04ec0a0ded9b4951c56dc68cef79026abf/numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958", size = 13492258 }, - { url = "https://files.pythonhosted.org/packages/bd/a7/2332679479c70b68dccbf4a8eb9c9b5ee383164b161bee9284ac141fbd33/numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8", size = 5090249 }, - { url = "https://files.pythonhosted.org/packages/c1/67/4aa00316b3b981a822c7a239d3a8135be2a6945d1fd11d0efb25d361711a/numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564", size = 6621704 }, - { url = "https://files.pythonhosted.org/packages/5e/da/1a429ae58b3b6c364eeec93bf044c532f2ff7b48a52e41050896cf15d5b1/numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512", size = 13606089 }, - { url = "https://files.pythonhosted.org/packages/9e/3e/3757f304c704f2f0294a6b8340fcf2be244038be07da4cccf390fa678a9f/numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b", size = 16043185 }, - { url = "https://files.pythonhosted.org/packages/43/97/75329c28fea3113d00c8d2daf9bc5828d58d78ed661d8e05e234f86f0f6d/numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc", size = 16410751 }, - { url = "https://files.pythonhosted.org/packages/ad/7a/442965e98b34e0ae9da319f075b387bcb9a1e0658276cc63adb8c9686f7b/numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0", size = 14082705 }, - { url = "https://files.pythonhosted.org/packages/ac/b6/26108cf2cfa5c7e03fb969b595c93131eab4a399762b51ce9ebec2332e80/numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9", size = 6239077 }, - { url = "https://files.pythonhosted.org/packages/a6/84/fa11dad3404b7634aaab50733581ce11e5350383311ea7a7010f464c0170/numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a", size = 12566858 }, - { url = "https://files.pythonhosted.org/packages/4d/0b/620591441457e25f3404c8057eb924d04f161244cb8a3680d529419aa86e/numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f", size = 20836263 }, - { url = "https://files.pythonhosted.org/packages/45/e1/210b2d8b31ce9119145433e6ea78046e30771de3fe353f313b2778142f34/numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598", size = 13507771 }, - { url = "https://files.pythonhosted.org/packages/55/44/aa9ee3caee02fa5a45f2c3b95cafe59c44e4b278fbbf895a93e88b308555/numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57", size = 5075805 }, - { url = "https://files.pythonhosted.org/packages/78/d6/61de6e7e31915ba4d87bbe1ae859e83e6582ea14c6add07c8f7eefd8488f/numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe", size = 6608380 }, - { url = "https://files.pythonhosted.org/packages/3e/46/48bdf9b7241e317e6cf94276fe11ba673c06d1fdf115d8b4ebf616affd1a/numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43", size = 13602451 }, - { url = "https://files.pythonhosted.org/packages/70/50/73f9a5aa0810cdccda9c1d20be3cbe4a4d6ea6bfd6931464a44c95eef731/numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56", size = 16039822 }, - { url = "https://files.pythonhosted.org/packages/ad/cd/098bc1d5a5bc5307cfc65ee9369d0ca658ed88fbd7307b0d49fab6ca5fa5/numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a", size = 16411822 }, - { url = "https://files.pythonhosted.org/packages/83/a2/7d4467a2a6d984549053b37945620209e702cf96a8bc658bc04bba13c9e2/numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef", size = 14079598 }, - { url = "https://files.pythonhosted.org/packages/e9/6a/d64514dcecb2ee70bfdfad10c42b76cab657e7ee31944ff7a600f141d9e9/numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f", size = 6236021 }, - { url = "https://files.pythonhosted.org/packages/bb/f9/12297ed8d8301a401e7d8eb6b418d32547f1d700ed3c038d325a605421a4/numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed", size = 12560405 }, - { url = "https://files.pythonhosted.org/packages/a7/45/7f9244cd792e163b334e3a7f02dff1239d2890b6f37ebf9e82cbe17debc0/numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f", size = 20859062 }, - { url = "https://files.pythonhosted.org/packages/b1/b4/a084218e7e92b506d634105b13e27a3a6645312b93e1c699cc9025adb0e1/numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4", size = 13515839 }, - { url = "https://files.pythonhosted.org/packages/27/45/58ed3f88028dcf80e6ea580311dc3edefdd94248f5770deb980500ef85dd/numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e", size = 5116031 }, - { url = "https://files.pythonhosted.org/packages/37/a8/eb689432eb977d83229094b58b0f53249d2209742f7de529c49d61a124a0/numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0", size = 6629977 }, - { url = "https://files.pythonhosted.org/packages/42/a3/5355ad51ac73c23334c7caaed01adadfda49544f646fcbfbb4331deb267b/numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408", size = 13575951 }, - { url = "https://files.pythonhosted.org/packages/c4/70/ea9646d203104e647988cb7d7279f135257a6b7e3354ea6c56f8bafdb095/numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6", size = 16022655 }, - { url = "https://files.pythonhosted.org/packages/14/ce/7fc0612903e91ff9d0b3f2eda4e18ef9904814afcae5b0f08edb7f637883/numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f", size = 16399902 }, - { url = "https://files.pythonhosted.org/packages/ef/62/1d3204313357591c913c32132a28f09a26357e33ea3c4e2fe81269e0dca1/numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17", size = 14067180 }, - { url = "https://files.pythonhosted.org/packages/24/d7/78a40ed1d80e23a774cb8a34ae8a9493ba1b4271dde96e56ccdbab1620ef/numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48", size = 6291907 }, - { url = "https://files.pythonhosted.org/packages/86/09/a5ab407bd7f5f5599e6a9261f964ace03a73e7c6928de906981c31c38082/numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4", size = 12644098 }, - { url = "https://files.pythonhosted.org/packages/00/e7/8d8bb791b62586cc432ecbb70632b4f23b7b7c88df41878de7528264f6d7/numpy-2.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f", size = 20983893 }, - { url = "https://files.pythonhosted.org/packages/5e/f3/cb8118a044b5007586245a650360c9f5915b2f4232dd7658bb7a63dd1d02/numpy-2.1.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4", size = 6752501 }, - { url = "https://files.pythonhosted.org/packages/53/f5/365b46439b518d2ec6ebb880cc0edf90f225145dfd4db7958334f7164530/numpy-2.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d", size = 16142601 }, - { url = "https://files.pythonhosted.org/packages/03/c2/d1fee6ba999aa7cd41ca6856937f2baaf604c3eec1565eae63451ec31e5e/numpy-2.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb", size = 12771397 }, -] - -[[package]] -name = "packaging" -version = "24.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, -] - -[[package]] -name = "pandas" -version = "2.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/70/c853aec59839bceed032d52010ff5f1b8d87dc3114b762e4ba2727661a3b/pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", size = 12580827 }, - { url = "https://files.pythonhosted.org/packages/99/f2/c4527768739ffa4469b2b4fff05aa3768a478aed89a2f271a79a40eee984/pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", size = 11303897 }, - { url = "https://files.pythonhosted.org/packages/ed/12/86c1747ea27989d7a4064f806ce2bae2c6d575b950be087837bdfcabacc9/pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", size = 66480908 }, - { url = "https://files.pythonhosted.org/packages/44/50/7db2cd5e6373ae796f0ddad3675268c8d59fb6076e66f0c339d61cea886b/pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", size = 13064210 }, - { url = "https://files.pythonhosted.org/packages/61/61/a89015a6d5536cb0d6c3ba02cebed51a95538cf83472975275e28ebf7d0c/pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", size = 16754292 }, - { url = "https://files.pythonhosted.org/packages/ce/0d/4cc7b69ce37fac07645a94e1d4b0880b15999494372c1523508511b09e40/pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", size = 14416379 }, - { url = "https://files.pythonhosted.org/packages/31/9e/6ebb433de864a6cd45716af52a4d7a8c3c9aaf3a98368e61db9e69e69a9c/pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", size = 11598471 }, - { url = "https://files.pythonhosted.org/packages/a8/44/d9502bf0ed197ba9bf1103c9867d5904ddcaf869e52329787fc54ed70cc8/pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", size = 12602222 }, - { url = "https://files.pythonhosted.org/packages/52/11/9eac327a38834f162b8250aab32a6781339c69afe7574368fffe46387edf/pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", size = 11321274 }, - { url = "https://files.pythonhosted.org/packages/45/fb/c4beeb084718598ba19aa9f5abbc8aed8b42f90930da861fcb1acdb54c3a/pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", size = 15579836 }, - { url = "https://files.pythonhosted.org/packages/cd/5f/4dba1d39bb9c38d574a9a22548c540177f78ea47b32f99c0ff2ec499fac5/pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc", size = 13058505 }, - { url = "https://files.pythonhosted.org/packages/b9/57/708135b90391995361636634df1f1130d03ba456e95bcf576fada459115a/pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3", size = 16744420 }, - { url = "https://files.pythonhosted.org/packages/86/4a/03ed6b7ee323cf30404265c284cee9c65c56a212e0a08d9ee06984ba2240/pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32", size = 14440457 }, - { url = "https://files.pythonhosted.org/packages/ed/8c/87ddf1fcb55d11f9f847e3c69bb1c6f8e46e2f40ab1a2d2abadb2401b007/pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5", size = 11617166 }, - { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893 }, - { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475 }, - { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645 }, - { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445 }, - { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235 }, - { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756 }, - { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 }, - { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 }, - { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 }, - { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 }, - { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 }, - { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 }, - { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 }, - { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 }, - { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 }, - { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 }, - { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 }, - { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 }, - { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 }, - { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, -] - -[[package]] -name = "pluggy" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, -] - -[[package]] -name = "pydata-sphinx-theme" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "accessible-pygments" }, - { name = "babel" }, - { name = "beautifulsoup4" }, - { name = "docutils" }, - { name = "pygments" }, - { name = "sphinx" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/c3/5240f2a5dc0b4856655c003466f70aa50d676b1709e5b04f0bee296bbd28/pydata_sphinx_theme-0.16.0.tar.gz", hash = "sha256:721dd26e05fa8b992d66ef545536e6cbe0110afb9865820a08894af1ad6f7707", size = 2407197 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/92/38f384061e1361fac7092c35e932c0e08026fb9080bf3fbf05f4c3bb6bda/pydata_sphinx_theme-0.16.0-py3-none-any.whl", hash = "sha256:18c810ee4e67e05281e371e156c1fb5bb0fa1f2747240461b225272f7d8d57d8", size = 6739948 }, -] - -[[package]] -name = "pygments" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, -] - -[[package]] -name = "pytest" -version = "8.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, -] - -[[package]] -name = "pytz" -version = "2024.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, -] - -[[package]] -name = "requests" -version = "2.32.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, -] - -[[package]] -name = "ruff" -version = "0.7.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/8b/bc4e0dfa1245b07cf14300e10319b98e958a53ff074c1dd86b35253a8c2a/ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2", size = 3275547 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/4b/f5094719e254829766b807dadb766841124daba75a37da83e292ae5ad12f/ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478", size = 10447512 }, - { url = "https://files.pythonhosted.org/packages/9e/1d/3d2d2c9f601cf6044799c5349ff5267467224cefed9b35edf5f1f36486e9/ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63", size = 10235436 }, - { url = "https://files.pythonhosted.org/packages/62/83/42a6ec6216ded30b354b13e0e9327ef75a3c147751aaf10443756cb690e9/ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20", size = 9888936 }, - { url = "https://files.pythonhosted.org/packages/4d/26/e1e54893b13046a6ad05ee9b89ee6f71542ba250f72b4c7a7d17c3dbf73d/ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109", size = 10697353 }, - { url = "https://files.pythonhosted.org/packages/21/24/98d2e109c4efc02bfef144ec6ea2c3e1217e7ce0cfddda8361d268dfd499/ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452", size = 10228078 }, - { url = "https://files.pythonhosted.org/packages/ad/b7/964c75be9bc2945fc3172241b371197bb6d948cc69e28bc4518448c368f3/ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea", size = 11264823 }, - { url = "https://files.pythonhosted.org/packages/12/8d/20abdbf705969914ce40988fe71a554a918deaab62c38ec07483e77866f6/ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7", size = 11951855 }, - { url = "https://files.pythonhosted.org/packages/b8/fc/6519ce58c57b4edafcdf40920b7273dfbba64fc6ebcaae7b88e4dc1bf0a8/ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05", size = 11516580 }, - { url = "https://files.pythonhosted.org/packages/37/1a/5ec1844e993e376a86eb2456496831ed91b4398c434d8244f89094758940/ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06", size = 12692057 }, - { url = "https://files.pythonhosted.org/packages/50/90/76867152b0d3c05df29a74bb028413e90f704f0f6701c4801174ba47f959/ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc", size = 11085137 }, - { url = "https://files.pythonhosted.org/packages/c8/eb/0a7cb6059ac3555243bd026bb21785bbc812f7bbfa95a36c101bd72b47ae/ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172", size = 10681243 }, - { url = "https://files.pythonhosted.org/packages/5e/76/2270719dbee0fd35780b05c08a07b7a726c3da9f67d9ae89ef21fc18e2e5/ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a", size = 10319187 }, - { url = "https://files.pythonhosted.org/packages/9f/e5/39100f72f8ba70bec1bd329efc880dea8b6c1765ea1cb9d0c1c5f18b8d7f/ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd", size = 10803715 }, - { url = "https://files.pythonhosted.org/packages/a5/89/40e904784f305fb56850063f70a998a64ebba68796d823dde67e89a24691/ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a", size = 11162912 }, - { url = "https://files.pythonhosted.org/packages/8d/1b/dd77503b3875c51e3dbc053fd8367b845ab8b01c9ca6d0c237082732856c/ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac", size = 8702767 }, - { url = "https://files.pythonhosted.org/packages/63/76/253ddc3e89e70165bba952ecca424b980b8d3c2598ceb4fc47904f424953/ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6", size = 9497534 }, - { url = "https://files.pythonhosted.org/packages/aa/70/f8724f31abc0b329ca98b33d73c14020168babcf71b0cba3cded5d9d0e66/ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f", size = 8851590 }, -] - -[[package]] -name = "six" -version = "1.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, -] - -[[package]] -name = "snowballstemmer" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 }, -] - -[[package]] -name = "soupsieve" -version = "2.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 }, -] - -[[package]] -name = "sphinx" -version = "8.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "alabaster" }, - { name = "babel" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "docutils" }, - { name = "imagesize" }, - { name = "jinja2" }, - { name = "packaging" }, - { name = "pygments" }, - { name = "requests" }, - { name = "snowballstemmer" }, - { name = "sphinxcontrib-applehelp" }, - { name = "sphinxcontrib-devhelp" }, - { name = "sphinxcontrib-htmlhelp" }, - { name = "sphinxcontrib-jsmath" }, - { name = "sphinxcontrib-qthelp" }, - { name = "sphinxcontrib-serializinghtml" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 }, -] - -[[package]] -name = "sphinx-pyproject" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "dom-toml" }, - { name = "domdf-python-tools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/39/97/aa8cec3da3e78f2c396b63332e2fe92fe43f7ff2ad19b3998735f28b0a7f/sphinx_pyproject-0.3.0.tar.gz", hash = "sha256:efc4ee9d96f579c4e4ed1ac273868c64565e88c8e37fe6ec2dc59fbcd57684ab", size = 7695 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/d5/89cb47c6399fd57ca451af15361499813c5d53e588cb6e00d89411ce724f/sphinx_pyproject-0.3.0-py3-none-any.whl", hash = "sha256:3aca968919f5ecd390f96874c3f64a43c9c7fcfdc2fd4191a781ad9228501b52", size = 23076 }, -] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, -] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, -] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, -] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, -] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, -] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, -] - -[[package]] -name = "tomli" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/e4/1b6cbcc82d8832dd0ce34767d5c560df8a3547ad8cbc427f34601415930a/tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8", size = 16622 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/f7/4da0ffe1892122c9ea096c57f64c2753ae5dd3ce85488802d11b0992cc6d/tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391", size = 13750 }, -] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, -] - -[[package]] -name = "tzdata" -version = "2024.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e1/34/943888654477a574a86a98e9896bae89c7aa15078ec29f490fef2f1e5384/tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", size = 193282 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586 }, -] - -[[package]] -name = "urllib3" -version = "2.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, -] From aaebf7c5cad03342aca2bda71dc93489b0967dc2 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 25 Nov 2024 12:24:07 -0600 Subject: [PATCH 31/59] Rename 'test' -> 'dev' in pyproject optional-dependencies --- .github/workflows/pytest-coverage.yaml | 2 +- python/README.md | 2 +- python/pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pytest-coverage.yaml b/.github/workflows/pytest-coverage.yaml index dcefc47..7b8800b 100644 --- a/.github/workflows/pytest-coverage.yaml +++ b/.github/workflows/pytest-coverage.yaml @@ -35,7 +35,7 @@ jobs: - name: Install Python dependencies working-directory: python shell: bash - run: uv pip install .[test,docs] + run: uv pip install .[dev,docs] - name: Run pytest working-directory: python diff --git a/python/README.md b/python/README.md index 5cb92d7..550d607 100644 --- a/python/README.md +++ b/python/README.md @@ -29,7 +29,7 @@ Create a development environment using [`uv`](https://docs.astral.sh/uv/): uv venv source .venv/bin/activate uv python install -uv pip install .[test] +uv pip install .[dev,docs] ``` ### Running tests diff --git a/python/pyproject.toml b/python/pyproject.toml index cf56c54..98fcbd0 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -13,7 +13,7 @@ dependencies = [ ] [project.optional-dependencies] -test = [ +dev = [ "mypy>=1.13.0", "pytest>=8.3.3", "ruff>=0.7.4", From cd5bc3ea453184e40d241cd8a1308798bb872d9c Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 25 Nov 2024 12:24:37 -0600 Subject: [PATCH 32/59] Switch order of authors in pyproject.toml --- python/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/pyproject.toml b/python/pyproject.toml index 98fcbd0..733619b 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -5,8 +5,8 @@ description = "Convenience Functions and Datasets for the Cook County Assessor's readme = "README.md" requires-python = ">=3.10" authors = [ - {name = "Dan Snow", email="daniel.snow@cookcountyil.gov"}, {name = "Jean Cochrane", email="jean.cochrane@cookcountyil.gov"}, + {name = "Dan Snow", email="daniel.snow@cookcountyil.gov"}, ] dependencies = [ "pandas>=2.2.3", From c8f4e492db48cbab145a261aa6bf221cd4cfd786 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 25 Nov 2024 12:27:14 -0600 Subject: [PATCH 33/59] Capitalize VAR_NAME_PREFIX constant in vars_funs.py --- python/ccao/vars_funs.py | 12 ++++++------ python/tests/test_vars_funs.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index a21f06c..146e756 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -11,7 +11,7 @@ vars_dict = pd.read_csv(str(_data_path / "vars_dict.csv")) # Prefix we use to identify variable name columns in the variable dictionary -var_name_prefix = "var_name" +VAR_NAME_PREFIX = "var_name" class OutputType(enum.Enum): @@ -98,17 +98,17 @@ def vars_rename( dictionary_var_columns = [ col for col in list(dictionary.columns.values) - if col.startswith(var_name_prefix) + if col.startswith(VAR_NAME_PREFIX) ] if not len(dictionary_var_columns) >= 2: raise ValueError( f"dictionary must contain at least two columns starting with " - f"{var_name_prefix}" + f"{VAR_NAME_PREFIX}" ) # Get a list of possible names_from and names_to from dictionary possible_names_args = [ - col.replace(f"{var_name_prefix}_", "") + col.replace(f"{VAR_NAME_PREFIX}_", "") for col in dictionary_var_columns ] @@ -136,8 +136,8 @@ def vars_rename( output_type = OutputType(output_type) # Get a mapping from names_from to names_to - from_ = f"{var_name_prefix}_{names_from}" - to = f"{var_name_prefix}_{names_to}" + from_ = f"{VAR_NAME_PREFIX}_{names_from}" + to = f"{VAR_NAME_PREFIX}_{names_to}" mapping = dict(zip(dictionary[from_], dictionary[to])) # Handle output differently depending on the input and output type args diff --git a/python/tests/test_vars_funs.py b/python/tests/test_vars_funs.py index 4b78c0f..699daeb 100644 --- a/python/tests/test_vars_funs.py +++ b/python/tests/test_vars_funs.py @@ -134,7 +134,7 @@ def test_vars_rename_invalid_dictionary_missing_variable_columns(): } ), ) - assert f"starting with {ccao.vars_funs.var_name_prefix}" in str(exc.value) + assert f"starting with {ccao.vars_funs.VAR_NAME_PREFIX}" in str(exc.value) @pytest.mark.parametrize("names_from,names_to", [(1, "pretty"), ("pretty", 1)]) From 81d8c207cf8e1d99a8ebd96122199aff662b8457 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 25 Nov 2024 13:26:11 -0600 Subject: [PATCH 34/59] Remove unnecessary OutputType enum from vars_funs.py --- python/ccao/vars_funs.py | 45 +++++++++------------------------- python/tests/test_vars_funs.py | 35 -------------------------- 2 files changed, 12 insertions(+), 68 deletions(-) diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index 146e756..244c96a 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -1,5 +1,4 @@ # Functions for translating variables between different data sources -import enum import importlib.resources import pandas as pd @@ -14,23 +13,11 @@ VAR_NAME_PREFIX = "var_name" -class OutputType(enum.Enum): - """Possible output types for variable renaming""" - - INPLACE = "inplace" - VECTOR = "vector" - - @classmethod - def values(cls) -> list[str]: - """Get the possible values for this enum""" - return [type_.value for type_ in cls] - - def vars_rename( data: list[str] | pd.DataFrame, names_from: str, names_to: str, - output_type: OutputType | str = OutputType.INPLACE, + output_type: str = "inplace", dictionary: pd.DataFrame | None = None, ) -> list[str] | pd.DataFrame: """ @@ -57,22 +44,22 @@ def vars_rename( :type names_to: str :param output_type: - Output type. Either `"inplace"`, which mutates the input data frame, - or `"vector"`, which returns a list of strings with the construction + Output type. Either ``"inplace"``, which mutates the input data frame, + or ``"vector"``, which returns a list of strings with the construction new_col_name = old_col_name. - :type output_type: OutputType or str + :type output_type: str :param dictionary: The dictionary for mapping column names. - Must contain keys like `var_name_` and `var_name_`. + Must contain keys like ``var_name_`` and ``var_name_``. :type dictionary: pandas.DataFrame :raises ValueError: If required arguments are invalid or the dictionary does not meet format requirements. - :raises TypeError: If `data` is neither a DataFrame nor a list of column names. + :raises TypeError: If ``data`` is neither a DataFrame nor a list of column names. :return: - Either the input data with renamed columns if `output_type` is - `"inplace"` and the input data is a DataFrame, otherwise a list of + Either the input data with renamed columns if ``output_type`` is + ``"inplace"`` and the input data is a DataFrame, otherwise a list of renamed columns. :rtype: pandas.DataFrame or list[str] @@ -123,17 +110,9 @@ def vars_rename( f"{label} must be one of {possible_names_args} (got '{var}')" ) - # Validate output type and convert it to the enum - if not isinstance(output_type, (OutputType, str)): - raise ValueError("output_type must be a string or OutputType instance") - - if isinstance(output_type, str): - if output_type not in OutputType.values(): - raise ValueError( - f"output_type must be one of {OutputType.values()} " - f"(got {output_type})" - ) - output_type = OutputType(output_type) + # Validate output type + if output_type not in ["inplace", "vector"]: + raise ValueError("output_type must be one of 'inplace' or 'vector'") # Get a mapping from names_from to names_to from_ = f"{VAR_NAME_PREFIX}_{names_from}" @@ -142,7 +121,7 @@ def vars_rename( # Handle output differently depending on the input and output type args if isinstance(data, pd.DataFrame): - if output_type == OutputType.INPLACE: + if output_type == "inplace": data.rename(columns=mapping, inplace=True) return data else: diff --git a/python/tests/test_vars_funs.py b/python/tests/test_vars_funs.py index 699daeb..f88bdea 100644 --- a/python/tests/test_vars_funs.py +++ b/python/tests/test_vars_funs.py @@ -59,30 +59,6 @@ def test_vars_rename_unmatched_cols_unchanged(): assert result == unmatched_colnames -@pytest.mark.parametrize( - "output_type", ["vector", ccao.vars_funs.OutputType.VECTOR] -) -def test_vars_rename_output_type_enum_or_string( - output_type, chars_sample_athena -): - # Both the enum and string versions of output_type should work - result = ccao.vars_rename( - data=chars_sample_athena.iloc[:, 13:19], - names_from="athena", - names_to="pretty", - output_type=output_type, - ) - expected = [ - "Apartments", - "Cathedral Ceiling", - "Attic Finish", - "Garage 1 Attached", - "Garage 1 Area Included", - "Garage 1 Size", - ] - assert result == expected - - def test_vars_rename_custom_dictionary(): result = ccao.vars_rename( data=["1", "2", "3"], @@ -157,17 +133,6 @@ def test_vars_rename_invalid_names_value(names_from, names_to): assert "must be one of" in str(exc.value) -def test_vars_rename_invalid_output_type_type(): - with pytest.raises(ValueError) as exc: - ccao.vars_rename( - data=["Apartments", "Cathedral Ceiling"], - names_from="pretty", - names_to="model", - output_type=1, - ) - assert "output_type must be a string or" in str(exc.value) - - def test_vars_rename_invalid_output_type_value(): with pytest.raises(ValueError) as exc: ccao.vars_rename( From b83786543b3a133c92f22b782624169ace6b7294 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 25 Nov 2024 13:35:40 -0600 Subject: [PATCH 35/59] Remove duplicative type checking in vars_funs.py --- python/ccao/vars_funs.py | 10 ++-------- python/tests/test_vars_funs.py | 26 +++----------------------- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index 244c96a..2a4ddec 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -78,7 +78,7 @@ def vars_rename( """ # Validate the dictionary schema dictionary = dictionary if dictionary is not None else vars_dict - if not isinstance(dictionary, pd.DataFrame) or len(dictionary) == 0: + if dictionary.empty: raise ValueError("dictionary must be a non-empty pandas DataFrame") # Make sure the dictionary contains variable columns @@ -99,10 +99,6 @@ def vars_rename( for col in dictionary_var_columns ] - # Validate names arguments - if not isinstance(names_from, str) or not isinstance(names_to, str): - raise ValueError("names_from and names_to must be strings") - # If names arguments aren't possible, throw error and list possible names for label, var in [("names_from", names_from), ("names_to", names_to)]: if var not in possible_names_args: @@ -126,9 +122,7 @@ def vars_rename( return data else: return [mapping.get(col, col) for col in list(data.columns.values)] - elif isinstance(data, list): + else: # If the input data is a list, it's not possible to update it inplace, # so ignore that argument return [mapping.get(col, col) for col in data] - else: - raise TypeError("data must be a DataFrame or list of column names") diff --git a/python/tests/test_vars_funs.py b/python/tests/test_vars_funs.py index f88bdea..496b993 100644 --- a/python/tests/test_vars_funs.py +++ b/python/tests/test_vars_funs.py @@ -75,17 +75,6 @@ def test_vars_rename_custom_dictionary(): assert result == expected -def test_vars_rename_invalid_dictionary_type(): - with pytest.raises(ValueError) as exc: - ccao.vars_rename( - data=["1", "2", "3"], - names_from="sql", - names_to="char", - dictionary={"sql": "char"}, - ) - assert "dictionary must be" in str(exc.value) - - def test_vars_rename_invalid_dictionary_empty(): with pytest.raises(ValueError) as exc: ccao.vars_rename( @@ -113,19 +102,10 @@ def test_vars_rename_invalid_dictionary_missing_variable_columns(): assert f"starting with {ccao.vars_funs.VAR_NAME_PREFIX}" in str(exc.value) -@pytest.mark.parametrize("names_from,names_to", [(1, "pretty"), ("pretty", 1)]) -def test_vars_rename_invalid_names_type(names_from, names_to): - with pytest.raises(ValueError) as exc: - ccao.vars_rename( - data=["1", "2", "3"], names_from=names_from, names_to=names_to - ) - assert "must be strings" in str(exc.value) - - @pytest.mark.parametrize( "names_from,names_to", [("1", "pretty"), ("pretty", "1")] ) -def test_vars_rename_invalid_names_value(names_from, names_to): +def test_vars_rename_invalid_names(names_from, names_to): with pytest.raises(ValueError) as exc: ccao.vars_rename( data=["1", "2", "3"], names_from=names_from, names_to=names_to @@ -133,7 +113,7 @@ def test_vars_rename_invalid_names_value(names_from, names_to): assert "must be one of" in str(exc.value) -def test_vars_rename_invalid_output_type_value(): +def test_vars_rename_invalid_output_type(): with pytest.raises(ValueError) as exc: ccao.vars_rename( data=["Apartments", "Cathedral Ceiling"], @@ -144,7 +124,7 @@ def test_vars_rename_invalid_output_type_value(): assert "output_type must be one of" in str(exc.value) -def test_vars_rename_invalid_data_type(): +def test_vars_rename_invalid_data(): with pytest.raises(TypeError) as exc: ccao.vars_rename(data=1, names_from="athena", names_to="pretty") assert str(exc.value).startswith("data must be") From 848db78a602ce7f777ea152faa83883fe98ebecb Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 25 Nov 2024 13:41:51 -0600 Subject: [PATCH 36/59] Wrap Python tests in classes for clearer organization --- .github/workflows/pytest-coverage.yaml | 2 +- python/tests/test_vars_funs.py | 208 ++++++++++++------------- 2 files changed, 102 insertions(+), 108 deletions(-) diff --git a/.github/workflows/pytest-coverage.yaml b/.github/workflows/pytest-coverage.yaml index 7b8800b..e758284 100644 --- a/.github/workflows/pytest-coverage.yaml +++ b/.github/workflows/pytest-coverage.yaml @@ -41,7 +41,7 @@ jobs: working-directory: python shell: bash run: | - pytest --doctest-modules \ + pytest -v --doctest-modules \ --junitxml=junit/test-results-${{ matrix.python-version }}.xmlpytest - name: Upload artifacts diff --git a/python/tests/test_vars_funs.py b/python/tests/test_vars_funs.py index 496b993..63c7c93 100644 --- a/python/tests/test_vars_funs.py +++ b/python/tests/test_vars_funs.py @@ -5,126 +5,120 @@ import ccao.vars_funs -@pytest.mark.parametrize("output_type", ["inplace", "vector"]) -def test_vars_rename_input_data_is_dataframe(output_type, chars_sample_athena): - data = chars_sample_athena.iloc[:, 13:19].copy() - result = ccao.vars_rename( - data=data, - names_from="athena", - names_to="pretty", - output_type=output_type, - ) - expected = [ - "Apartments", - "Cathedral Ceiling", - "Attic Finish", - "Garage 1 Attached", - "Garage 1 Area Included", - "Garage 1 Size", - ] - if output_type == "inplace": - assert list(result.columns) == expected - else: +class TestVarsRename: + @pytest.mark.parametrize("output_type", ["inplace", "vector"]) + def test_vars_rename_input_data_is_dataframe( + self, output_type, chars_sample_athena + ): + data = chars_sample_athena.iloc[:, 13:19].copy() + result = ccao.vars_rename( + data=data, + names_from="athena", + names_to="pretty", + output_type=output_type, + ) + expected = [ + "Apartments", + "Cathedral Ceiling", + "Attic Finish", + "Garage 1 Attached", + "Garage 1 Area Included", + "Garage 1 Size", + ] + if output_type == "inplace": + assert list(result.columns) == expected + else: + assert result == expected + + @pytest.mark.parametrize("output_type", ["inplace", "vector"]) + def test_vars_rename_input_data_is_list(self, output_type): + result = ccao.vars_rename( + data=["Apartments", "Cathedral Ceiling"], + names_from="pretty", + names_to="model", + output_type=output_type, + ) + expected = ["char_apts", "char_tp_dsgn"] + # Output should be the same regardless of the value of `output_type` assert result == expected - -@pytest.mark.parametrize("output_type", ["inplace", "vector"]) -def test_vars_rename_input_data_is_list(output_type): - result = ccao.vars_rename( - data=["Apartments", "Cathedral Ceiling"], - names_from="pretty", - names_to="model", - output_type=output_type, - ) - expected = ["char_apts", "char_tp_dsgn"] - # Output should be the same regardless of the value of `output_type` - assert result == expected - - -def test_vars_rename_hie_to_athena(chars_sample_hie): - data = chars_sample_hie.iloc[:, 1:3].copy() - result = ccao.vars_rename( - data=data, names_from="hie", names_to="athena", output_type="vector" - ) - expected = ["township_code", "card"] - assert result == expected - - -def test_vars_rename_unmatched_cols_unchanged(): - # If columns are not present in the dictionary, leave them as-is - unmatched_colnames = ["foo", "bar", "baz"] - result = ccao.vars_rename( - data=unmatched_colnames, names_from="hie", names_to="athena" - ) - assert result == unmatched_colnames - - -def test_vars_rename_custom_dictionary(): - result = ccao.vars_rename( - data=["1", "2", "3"], - names_from="foo", - names_to="bar", - dictionary=pd.DataFrame( - { - "var_name_foo": ["1", "2", "3"], - "var_name_bar": ["char_1", "char_2", "char_3"], - } - ), - ) - expected = ["char_1", "char_2", "char_3"] - assert result == expected - - -def test_vars_rename_invalid_dictionary_empty(): - with pytest.raises(ValueError) as exc: - ccao.vars_rename( - data=["1", "2", "3"], - names_from="sql", - names_to="char", - dictionary=pd.DataFrame(), + def test_vars_rename_hie_to_athena(self, chars_sample_hie): + data = chars_sample_hie.iloc[:, 1:3].copy() + result = ccao.vars_rename( + data=data, + names_from="hie", + names_to="athena", + output_type="vector", ) - assert "non-empty" in str(exc.value) + expected = ["township_code", "card"] + assert result == expected + def test_vars_rename_unmatched_cols_unchanged(self): + # If columns are not present in the dictionary, leave them as-is + unmatched_colnames = ["foo", "bar", "baz"] + result = ccao.vars_rename( + data=unmatched_colnames, names_from="hie", names_to="athena" + ) + assert result == unmatched_colnames -def test_vars_rename_invalid_dictionary_missing_variable_columns(): - with pytest.raises(ValueError) as exc: - ccao.vars_rename( + def test_vars_rename_custom_dictionary(self): + result = ccao.vars_rename( data=["1", "2", "3"], names_from="foo", names_to="bar", dictionary=pd.DataFrame( { - "foo": ["1", "2", "3"], - "bar": ["char_1", "char_2", "char_3"], + "var_name_foo": ["1", "2", "3"], + "var_name_bar": ["char_1", "char_2", "char_3"], } ), ) - assert f"starting with {ccao.vars_funs.VAR_NAME_PREFIX}" in str(exc.value) - - -@pytest.mark.parametrize( - "names_from,names_to", [("1", "pretty"), ("pretty", "1")] -) -def test_vars_rename_invalid_names(names_from, names_to): - with pytest.raises(ValueError) as exc: - ccao.vars_rename( - data=["1", "2", "3"], names_from=names_from, names_to=names_to - ) - assert "must be one of" in str(exc.value) - + expected = ["char_1", "char_2", "char_3"] + assert result == expected -def test_vars_rename_invalid_output_type(): - with pytest.raises(ValueError) as exc: - ccao.vars_rename( - data=["Apartments", "Cathedral Ceiling"], - names_from="pretty", - names_to="model", - output_type="foo", + def test_vars_rename_invalid_dictionary_empty(self): + with pytest.raises(ValueError) as exc: + ccao.vars_rename( + data=["1", "2", "3"], + names_from="sql", + names_to="char", + dictionary=pd.DataFrame(), + ) + assert "non-empty" in str(exc.value) + + def test_vars_rename_invalid_dictionary_missing_variable_columns(self): + with pytest.raises(ValueError) as exc: + ccao.vars_rename( + data=["1", "2", "3"], + names_from="foo", + names_to="bar", + dictionary=pd.DataFrame( + { + "foo": ["1", "2", "3"], + "bar": ["char_1", "char_2", "char_3"], + } + ), + ) + assert f"starting with {ccao.vars_funs.VAR_NAME_PREFIX}" in str( + exc.value ) - assert "output_type must be one of" in str(exc.value) - -def test_vars_rename_invalid_data(): - with pytest.raises(TypeError) as exc: - ccao.vars_rename(data=1, names_from="athena", names_to="pretty") - assert str(exc.value).startswith("data must be") + @pytest.mark.parametrize( + "names_from,names_to", [("1", "pretty"), ("pretty", "1")] + ) + def test_vars_rename_invalid_names(self, names_from, names_to): + with pytest.raises(ValueError) as exc: + ccao.vars_rename( + data=["1", "2", "3"], names_from=names_from, names_to=names_to + ) + assert "must be one of" in str(exc.value) + + def test_vars_rename_invalid_output_type(self): + with pytest.raises(ValueError) as exc: + ccao.vars_rename( + data=["Apartments", "Cathedral Ceiling"], + names_from="pretty", + names_to="model", + output_type="foo", + ) + assert "output_type must be one of" in str(exc.value) From e66eff3174c01adcac21c01631343a4f25f1247f Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 25 Nov 2024 20:01:06 +0000 Subject: [PATCH 37/59] Change chars_sample fixtures to symlinks to R data in Python package --- data-raw/chars_sample.R | 4 ++ data-raw/chars_sample_athena.csv | 71 ++++++++++++++++++ data-raw/chars_sample_hie.csv | 12 ++++ python/tests/fixtures/chars_sample_athena.csv | 72 +------------------ python/tests/fixtures/chars_sample_hie.csv | 13 +--- 5 files changed, 89 insertions(+), 83 deletions(-) create mode 100644 data-raw/chars_sample_athena.csv create mode 100644 data-raw/chars_sample_hie.csv mode change 100755 => 120000 python/tests/fixtures/chars_sample_athena.csv mode change 100755 => 120000 python/tests/fixtures/chars_sample_hie.csv diff --git a/data-raw/chars_sample.R b/data-raw/chars_sample.R index fa09860..52907c6 100644 --- a/data-raw/chars_sample.R +++ b/data-raw/chars_sample.R @@ -54,6 +54,9 @@ chars_sample_athena <- dbGetQuery( " ) usethis::use_data(chars_sample_athena, overwrite = TRUE) +# Write the data to CSV so that the Python version of the package can consume +# it as well +readr::write_csv(chars_sample_athena, "data-raw/chars_sample_athena.csv") # Get sample ADDCHARS data chars_sample_hie <- dbGetQuery( @@ -69,3 +72,4 @@ chars_sample_hie <- dbGetQuery( " ) usethis::use_data(chars_sample_hie, overwrite = TRUE) +readr::write_csv(chars_sample_hie, "data-raw/chars_sample_hie.csv") diff --git a/data-raw/chars_sample_athena.csv b/data-raw/chars_sample_athena.csv new file mode 100644 index 0000000..12447a8 --- /dev/null +++ b/data-raw/chars_sample_athena.csv @@ -0,0 +1,71 @@ +pin,year,class,char_yrblt,char_bldg_sf,char_land_sf,char_beds,char_rooms,char_fbath,char_hbath,char_frpl,char_type_resd,char_cnst_qlty,char_apts,char_tp_dsgn,char_attic_fnsh,char_gar1_att,char_gar1_area,char_gar1_size,char_gar1_cnst,char_attic_type,char_bsmt,char_ext_wall,char_heat,char_repair_cnd,char_bsmt_fin,char_roof_cnst,char_use,char_age,char_site,char_ncu,char_renovation,char_porch,char_air,char_tp_plan +10254170360000,2015,205,1948,1775,4340,4,7,2,1,2,2,2,6,2,3,2,2,7,0,3,1,2,1,2,1,1,1,67,2,0,0,0,1,2 +09363230550000,2019,203,1923,1200,4375,3,5,1,1,0,1,2,6,2,3,2,2,7,0,3,1,3,2,2,3,1,1,95,2,0,0,1,2,2 +09363230550000,2016,203,1923,1200,4375,3,5,1,1,0,1,2,6,2,3,2,2,7,0,3,1,3,2,2,3,1,1,92,2,0,0,1,2,2 +14321260280000,2018,211,1878,2850,3125,4,9,3,0,0,3,2,2,2,3,2,2,7,0,3,3,2,1,2,1,2,2,140,2,0,0,0,2,2 +10253190450000,2018,204,1951,2469,11160,3,8,2,1,1,1,2,6,0,3,1,2,3,2,3,3,2,1,2,3,1,1,67,2,0,0,0,1,2 +14321260280000,2016,211,1878,2850,3125,4,9,3,0,0,3,2,2,2,3,2,2,7,0,3,3,2,1,2,1,2,2,137,2,0,0,0,2,2 +09254040180000,2020,203,1955,1571,3750,3,6,1,0,0,5,2,6,2,1,2,2,7,0,1,1,2,1,2,3,1,1,63,2,0,0,0,1,2 +13362270230000,2015,202,1890,756,2750,2,4,1,1,0,1,2,6,0,3,2,2,3,1,1,1,3,2,3,1,1,1,125,2,0,0,1,2,2 +13253160160000,2020,202,1904,706,3150,2,5,1,0,0,1,2,6,2,3,2,2,3,1,1,1,2,2,2,3,1,1,117,2,0,0,0,2,2 +10254170360000,2018,205,1948,1829,4340,3,7,2,1,2,2,2,6,2,3,2,2,3,1,3,1,2,1,2,1,1,1,70,2,0,0,0,1,2 +13253160160000,2017,202,1901,706,3150,2,5,1,0,0,1,2,6,2,3,2,2,3,1,1,1,2,2,2,3,1,1,114,2,0,0,0,2,2 +09363230550000,2017,203,1923,1200,4375,3,5,1,1,0,1,2,6,2,3,2,2,7,0,3,1,3,2,2,3,1,1,92,2,0,0,1,2,2 +10253190450000,2017,204,1951,2469,11160,3,8,2,1,1,1,2,6,0,3,1,2,3,2,3,3,2,1,2,3,1,1,64,2,0,0,0,1,2 +13253230040000,2019,203,1969,1040,3150,2,5,1,0,0,1,2,6,2,3,2,2,7,0,3,1,2,1,2,3,1,1,49,2,0,0,0,2,2 +17032010190000,2016,211,1887,3299,1204,8,11,3,0,2,3,2,2,2,3,2,2,7,0,3,1,2,2,2,1,6,2,128,2,0,0,0,1,2 +09361030150000,2016,203,1928,1200,5280,2,5,1,0,0,1,2,6,0,3,1,1,1,1,1,1,3,2,2,3,1,1,87,3,0,0,0,2,0 +09361030150000,2015,203,1928,1200,5280,2,5,1,0,0,1,2,6,0,3,1,1,1,1,1,1,3,2,2,3,1,1,87,3,0,0,0,2,0 +17032010190000,2015,211,1887,3299,1204,8,11,3,0,2,3,2,2,2,3,2,2,7,0,3,1,2,2,2,1,6,2,128,2,0,0,0,1,2 +10253190450000,2021,209,1954,6027,11160,5,12,4,1,1,2,2,6,2,3,1,2,3,2,3,3,4,1,2,3,1,1,70,2,0,0,0,1,2 +09254040180000,2018,203,1955,1571,3750,3,6,1,0,0,5,2,6,2,1,2,2,7,0,1,1,2,1,2,3,1,1,63,2,0,0,0,1,2 +09254040180000,2017,203,1955,1571,3750,3,6,1,0,0,5,2,6,2,1,2,2,7,0,1,1,2,1,2,3,1,1,60,2,0,0,0,1,2 +13362270230000,2016,202,1890,756,2750,2,4,1,1,0,1,2,6,0,3,2,2,3,1,1,1,3,2,3,1,1,1,125,2,0,0,1,2,2 +13253230040000,2021,278,1972,2210,3150,4,9,3,0,0,2,2,6,2,3,2,2,7,0,3,1,2,1,2,1,1,1,52,2,0,0,0,1,2 +09254040180000,2015,203,1955,1571,3750,3,6,1,0,0,5,2,6,2,1,2,2,7,0,1,1,2,1,2,3,1,1,60,2,0,0,0,1,2 +13362270230000,2017,202,1890,756,2750,2,4,1,1,0,1,2,6,0,3,2,2,3,1,1,1,3,2,3,1,1,1,125,2,0,0,1,2,2 +13253230040000,2015,203,1969,1040,3150,2,5,1,0,0,1,2,6,0,3,2,2,3,1,3,2,2,1,2,3,1,1,46,2,0,0,0,2,2 +13253160160000,2019,202,1901,706,3150,2,5,1,0,0,1,2,6,2,3,2,2,3,1,1,1,2,2,2,3,1,1,117,2,0,0,0,2,2 +10254170360000,2017,205,1948,1829,4340,3,7,2,1,2,2,2,6,2,3,2,2,3,1,3,1,2,1,2,1,1,1,67,2,0,0,0,1,2 +10253190450000,2019,204,1951,2469,11160,3,8,2,1,1,1,2,6,0,3,1,2,3,2,3,3,2,1,2,3,1,1,67,2,0,0,0,1,2 +14321260280000,2021,206,1880,3660,3125,4,11,5,0,0,3,2,6,2,3,2,2,4,2,3,3,2,1,2,1,2,1,143,2,0,1,0,2,2 +09361030150000,2018,203,1928,1357,5280,2,6,1,1,0,1,2,6,2,3,2,2,1,1,3,1,2,2,2,3,1,1,90,3,0,0,0,2,2 +09361030150000,2021,206,1931,2772,5280,5,9,3,1,0,2,2,6,2,3,2,2,1,1,3,1,3,2,2,3,1,1,93,3,0,0,0,1,2 +17032010190000,2021,210,1887,3299,1204,8,11,3,1,2,3,2,6,2,3,2,2,7,0,3,1,2,2,2,1,6,1,134,2,0,1,0,1,2 +13253160160000,2021,205,1904,1790,3150,3,7,3,0,0,2,2,6,2,3,2,2,3,1,1,1,2,2,2,1,1,1,120,2,0,1,0,1,2 +10254170360000,2021,206,1959,2312,4340,4,8,3,1,2,2,2,6,2,3,2,2,3,1,3,1,2,1,2,1,1,1,73,2,0,0,0,1,2 +13362270230000,2020,202,1890,756,2750,2,4,1,1,0,1,2,6,0,3,2,2,3,1,1,1,3,2,3,1,1,1,128,2,0,0,1,2,2 +09361030150000,2017,203,1928,1357,5280,2,6,1,1,0,1,2,6,2,3,2,2,1,1,3,1,2,2,2,3,1,1,87,3,0,0,0,2,2 +17032010190000,2017,211,1887,3299,1204,8,11,3,0,2,3,2,2,2,3,2,2,7,0,3,1,2,2,2,1,6,2,128,2,0,0,0,1,2 +13253230040000,2017,203,1969,1040,3150,2,5,1,0,0,1,2,6,2,3,2,2,3,1,3,1,2,1,2,3,1,1,46,2,0,0,0,2,2 +17032010190000,2019,211,1888,3299,1204,8,11,3,0,2,3,2,2,2,3,2,2,7,0,3,1,2,2,2,1,6,2,131,2,0,0,0,1,2 +09361030150000,2019,203,1928,1357,5280,2,6,1,1,0,1,2,6,2,3,2,2,1,1,3,1,2,2,2,3,1,1,90,3,0,0,0,2,2 +09363230550000,2015,203,1923,1200,4375,3,5,1,1,0,1,2,6,2,3,2,2,7,0,3,1,3,2,2,3,1,1,92,2,0,0,1,2,2 +09254040180000,2016,203,1955,1571,3750,3,6,1,0,0,5,2,6,2,1,2,2,7,0,1,1,2,1,2,3,1,1,60,2,0,0,0,1,2 +13362270230000,2019,202,1890,756,2750,2,4,1,1,0,1,2,6,0,3,2,2,3,1,1,1,3,2,3,1,1,1,128,2,0,0,1,2,2 +13253160160000,2018,202,1901,706,3150,2,5,1,0,0,1,2,6,2,3,2,2,3,1,1,1,2,2,2,3,1,1,117,2,0,0,0,2,2 +09363230550000,2021,206,1926,2376,4375,4,7,3,1,0,2,2,6,2,3,2,2,3,1,3,1,3,2,2,3,1,1,98,2,0,0,1,1,2 +13253230040000,2016,203,1969,1040,3150,2,5,1,0,0,1,2,6,0,3,2,2,3,1,3,2,2,1,2,3,1,1,46,2,0,0,0,2,2 +10254170360000,2016,205,1948,1775,4340,4,7,2,1,2,2,2,6,2,3,2,2,7,0,3,1,2,1,2,1,1,1,67,2,0,0,0,1,2 +13362270230000,2021,205,1893,1476,2750,4,6,3,1,0,2,2,6,2,3,2,2,3,1,1,1,3,2,3,1,1,1,131,2,0,1,1,1,2 +17032010190000,2018,211,1887,3299,1204,8,11,3,0,2,3,2,2,2,3,2,2,7,0,3,1,2,2,2,1,6,2,131,2,0,0,0,1,2 +14321260280000,2015,211,1878,2850,3125,6,15,3,0,0,3,2,2,0,3,2,2,7,0,3,1,2,2,2,3,2,2,137,2,0,0,1,2,2 +17032010190000,2020,211,1888,3299,1204,8,11,3,0,2,3,2,2,2,3,2,2,7,0,3,1,2,2,2,1,6,2,131,2,0,0,0,1,2 +13253160160000,2015,202,1901,706,3150,2,5,1,0,0,1,2,6,2,3,2,2,3,1,1,1,2,2,2,3,1,1,114,2,0,0,1,2,2 +09363230550000,2018,203,1923,1200,4375,3,5,1,1,0,1,2,6,2,3,2,2,7,0,3,1,3,2,2,3,1,1,95,2,0,0,1,2,2 +09254040180000,2019,203,1955,1571,3750,3,6,1,0,0,5,2,6,2,1,2,2,7,0,1,1,2,1,2,3,1,1,63,2,0,0,0,1,2 +10253190450000,2020,204,1951,2469,11160,3,8,2,1,1,1,2,6,0,3,1,2,3,2,3,3,2,1,2,3,1,1,67,2,0,0,0,1,2 +09361030150000,2020,203,1928,1357,5280,2,6,1,1,0,1,2,6,2,3,2,2,1,1,3,1,2,2,2,3,1,1,90,3,0,0,0,2,2 +13362270230000,2018,202,1890,756,2750,2,4,1,1,0,1,2,6,0,3,2,2,3,1,1,1,3,2,3,1,1,1,128,2,0,0,1,2,2 +10253190450000,2015,204,1951,2469,11160,3,8,2,1,1,1,2,6,0,3,1,2,3,2,3,3,2,1,2,3,1,1,64,2,0,0,0,1,2 +13253230040000,2018,203,1969,1040,3150,2,5,1,0,0,1,2,6,2,3,2,2,3,1,3,1,2,1,2,3,1,1,49,2,0,0,0,2,2 +10253190450000,2016,204,1951,2469,11160,3,8,2,1,1,1,2,6,0,3,1,2,3,2,3,3,2,1,2,3,1,1,64,2,0,0,0,1,2 +14321260280000,2019,211,1878,2850,3125,4,9,3,0,0,3,2,2,2,3,2,2,7,0,3,3,2,1,2,1,2,2,140,2,0,0,0,2,2 +13253160160000,2016,202,1901,706,3150,2,5,1,0,0,1,2,6,2,3,2,2,3,1,1,1,2,2,2,3,1,1,114,2,0,0,0,2,2 +09363230550000,2020,203,1923,1200,4375,3,5,1,1,0,1,2,6,2,3,2,2,7,0,3,1,3,2,2,3,1,1,95,2,0,0,1,2,2 +10254170360000,2019,205,1948,1829,4340,3,7,2,1,2,2,2,6,2,3,2,2,3,1,3,1,2,1,2,1,1,1,70,2,0,0,0,1,2 +13253230040000,2020,203,1969,1040,3150,2,5,1,0,0,1,2,6,2,3,2,2,7,0,3,1,2,1,2,3,1,1,49,2,0,0,0,2,2 +10254170360000,2020,205,1948,1829,4340,3,7,2,1,2,2,2,6,2,3,2,2,3,1,3,1,2,1,2,1,1,1,70,2,0,0,0,1,2 +09254040180000,2021,203,1958,1568,3750,3,8,3,0,0,6,2,6,2,1,2,2,3,1,1,1,2,1,2,1,1,1,66,2,0,0,0,1,2 +14321260280000,2020,211,1878,2850,3125,4,9,3,0,0,3,2,2,2,3,2,2,7,0,3,3,2,1,2,1,2,2,140,2,0,0,0,2,2 +14321260280000,2017,211,1878,2850,3125,4,9,3,0,0,3,2,2,2,3,2,2,7,0,3,3,2,1,2,1,2,2,137,2,0,0,0,2,2 diff --git a/data-raw/chars_sample_hie.csv b/data-raw/chars_sample_hie.csv new file mode 100644 index 0000000..f8ed4cc --- /dev/null +++ b/data-raw/chars_sample_hie.csv @@ -0,0 +1,12 @@ +pin,qu_town,qu_mlt_cd,qu_home_improvement,qu_use,qu_exterior_wall,qu_roof,qu_basement_type,qu_basement_finish,qu_heat,qu_air,qu_attic_type,qu_attic_finish,qu_type_plan,qu_type_design,qu_construct_quality,qu_porch,qu_garage_size,qu_garage_const,qu_garage_attached,qu_garage_area,qu_num_apts,qu_sqft_bld,qu_lnd_sqft,qu_class,qu_rooms,qu_beds,qu_full_bath,qu_half_bath,qu_fire_place,qu_no_com_unit,qu_type_of_res,qu_upload_date,hie_last_year_active,year +13253230040000,77,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,2,2,0,0,0,203,0,0,0,0,0,0,0,2019-06-19,2023,2019 +10254170360000,75,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,483,0,206,1,1,1,0,0,0,0,2017-01-23,2020,2017 +13253230040000,77,3,1,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1170,0,278,4,2,2,0,0,0,2,2017-04-20,2020,2017 +09361030150000,71,3,1,0,3,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1415,0,206,3,3,2,0,0,0,2,2017-06-09,2020,2017 +13362270230000,77,3,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,720,0,205,2,2,2,0,0,0,2,2015-07-01,2020,2015 +09254040180000,71,3,1,0,0,0,0,0,0,1,0,0,0,0,0,0,3,1,2,2,0,845,0,207,2,2,2,0,0,0,2,2015-08-01,2020,2015 +09363230550000,71,3,1,0,0,0,0,0,0,1,0,0,0,0,0,0,3,1,2,2,0,1176,0,206,2,1,2,0,0,0,2,2015-08-01,2020,2015 +17032010190000,74,3,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,210,0,0,0,1,0,0,0,2015-09-02,2020,2015 +10253190450000,75,3,1,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3558,0,209,4,2,2,0,0,0,2,2016-02-03,2020,2016 +13253160160000,77,3,1,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1084,0,205,2,1,2,0,0,0,2,2016-08-01,2020,2016 +14321260280000,74,3,1,1,0,0,0,0,0,0,0,0,0,0,0,0,4,2,2,2,6,810,0,206,2,0,2,0,0,0,0,2016-09-13,2020,2016 diff --git a/python/tests/fixtures/chars_sample_athena.csv b/python/tests/fixtures/chars_sample_athena.csv deleted file mode 100755 index 3fb5c3a..0000000 --- a/python/tests/fixtures/chars_sample_athena.csv +++ /dev/null @@ -1,71 +0,0 @@ -"pin","year","class","char_yrblt","char_bldg_sf","char_land_sf","char_beds","char_rooms","char_fbath","char_hbath","char_frpl","char_type_resd","char_cnst_qlty","char_apts","char_tp_dsgn","char_attic_fnsh","char_gar1_att","char_gar1_area","char_gar1_size","char_gar1_cnst","char_attic_type","char_bsmt","char_ext_wall","char_heat","char_repair_cnd","char_bsmt_fin","char_roof_cnst","char_use","char_age","char_site","char_ncu","char_renovation","char_porch","char_air","char_tp_plan" -"10254170360000","2015","205",1948,1775,4340,4,7,2,1,2,2,"2","6","2","3","2","2","7","0","3","1","2","1","2","1","1","1","67","2","0","0","0","1","2" -"09363230550000","2019","203",1923,1200,4375,3,5,1,1,0,1,"2","6","2","3","2","2","7","0","3","1","3","2","2","3","1","1","95","2","0","0","1","2","2" -"09363230550000","2016","203",1923,1200,4375,3,5,1,1,0,1,"2","6","2","3","2","2","7","0","3","1","3","2","2","3","1","1","92","2","0","0","1","2","2" -"14321260280000","2018","211",1878,2850,3125,4,9,3,0,0,3,"2","2","2","3","2","2","7","0","3","3","2","1","2","1","2","2","140","2","0","0","0","2","2" -"10253190450000","2018","204",1951,2469,11160,3,8,2,1,1,1,"2","6","0","3","1","2","3","2","3","3","2","1","2","3","1","1","67","2","0","0","0","1","2" -"14321260280000","2016","211",1878,2850,3125,4,9,3,0,0,3,"2","2","2","3","2","2","7","0","3","3","2","1","2","1","2","2","137","2","0","0","0","2","2" -"09254040180000","2020","203",1955,1571,3750,3,6,1,0,0,5,"2","6","2","1","2","2","7","0","1","1","2","1","2","3","1","1","63","2","0","0","0","1","2" -"13362270230000","2015","202",1890,756,2750,2,4,1,1,0,1,"2","6","0","3","2","2","3","1","1","1","3","2","3","1","1","1","125","2","0","0","1","2","2" -"13253160160000","2020","202",1904,706,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","1","1","2","2","2","3","1","1","117","2","0","0","0","2","2" -"10254170360000","2018","205",1948,1829,4340,3,7,2,1,2,2,"2","6","2","3","2","2","3","1","3","1","2","1","2","1","1","1","70","2","0","0","0","1","2" -"13253160160000","2017","202",1901,706,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","1","1","2","2","2","3","1","1","114","2","0","0","0","2","2" -"09363230550000","2017","203",1923,1200,4375,3,5,1,1,0,1,"2","6","2","3","2","2","7","0","3","1","3","2","2","3","1","1","92","2","0","0","1","2","2" -"10253190450000","2017","204",1951,2469,11160,3,8,2,1,1,1,"2","6","0","3","1","2","3","2","3","3","2","1","2","3","1","1","64","2","0","0","0","1","2" -"13253230040000","2019","203",1969,1040,3150,2,5,1,0,0,1,"2","6","2","3","2","2","7","0","3","1","2","1","2","3","1","1","49","2","0","0","0","2","2" -"17032010190000","2016","211",1887,3299,1204,8,11,3,0,2,3,"2","2","2","3","2","2","7","0","3","1","2","2","2","1","6","2","128","2","0","0","0","1","2" -"09361030150000","2016","203",1928,1200,5280,2,5,1,0,0,1,"2","6","0","3","1","1","1","1","1","1","3","2","2","3","1","1","87","3","0","0","0","2","0" -"09361030150000","2015","203",1928,1200,5280,2,5,1,0,0,1,"2","6","0","3","1","1","1","1","1","1","3","2","2","3","1","1","87","3","0","0","0","2","0" -"17032010190000","2015","211",1887,3299,1204,8,11,3,0,2,3,"2","2","2","3","2","2","7","0","3","1","2","2","2","1","6","2","128","2","0","0","0","1","2" -"10253190450000","2021","209",1954,6027,11160,5,12,4,1,1,2,"2","6","2","3","1","2","3","2","3","3","4","1","2","3","1","1","70","2","0","0","0","1","2" -"09254040180000","2018","203",1955,1571,3750,3,6,1,0,0,5,"2","6","2","1","2","2","7","0","1","1","2","1","2","3","1","1","63","2","0","0","0","1","2" -"09254040180000","2017","203",1955,1571,3750,3,6,1,0,0,5,"2","6","2","1","2","2","7","0","1","1","2","1","2","3","1","1","60","2","0","0","0","1","2" -"13362270230000","2016","202",1890,756,2750,2,4,1,1,0,1,"2","6","0","3","2","2","3","1","1","1","3","2","3","1","1","1","125","2","0","0","1","2","2" -"13253230040000","2021","278",1972,2210,3150,4,9,3,0,0,2,"2","6","2","3","2","2","7","0","3","1","2","1","2","1","1","1","52","2","0","0","0","1","2" -"09254040180000","2015","203",1955,1571,3750,3,6,1,0,0,5,"2","6","2","1","2","2","7","0","1","1","2","1","2","3","1","1","60","2","0","0","0","1","2" -"13362270230000","2017","202",1890,756,2750,2,4,1,1,0,1,"2","6","0","3","2","2","3","1","1","1","3","2","3","1","1","1","125","2","0","0","1","2","2" -"13253230040000","2015","203",1969,1040,3150,2,5,1,0,0,1,"2","6","0","3","2","2","3","1","3","2","2","1","2","3","1","1","46","2","0","0","0","2","2" -"13253160160000","2019","202",1901,706,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","1","1","2","2","2","3","1","1","117","2","0","0","0","2","2" -"10254170360000","2017","205",1948,1829,4340,3,7,2,1,2,2,"2","6","2","3","2","2","3","1","3","1","2","1","2","1","1","1","67","2","0","0","0","1","2" -"10253190450000","2019","204",1951,2469,11160,3,8,2,1,1,1,"2","6","0","3","1","2","3","2","3","3","2","1","2","3","1","1","67","2","0","0","0","1","2" -"14321260280000","2021","206",1880,3660,3125,4,11,5,0,0,3,"2","6","2","3","2","2","4","2","3","3","2","1","2","1","2","1","143","2","0","1","0","2","2" -"09361030150000","2018","203",1928,1357,5280,2,6,1,1,0,1,"2","6","2","3","2","2","1","1","3","1","2","2","2","3","1","1","90","3","0","0","0","2","2" -"09361030150000","2021","206",1931,2772,5280,5,9,3,1,0,2,"2","6","2","3","2","2","1","1","3","1","3","2","2","3","1","1","93","3","0","0","0","1","2" -"17032010190000","2021","210",1887,3299,1204,8,11,3,1,2,3,"2","6","2","3","2","2","7","0","3","1","2","2","2","1","6","1","134","2","0","1","0","1","2" -"13253160160000","2021","205",1904,1790,3150,3,7,3,0,0,2,"2","6","2","3","2","2","3","1","1","1","2","2","2","1","1","1","120","2","0","1","0","1","2" -"10254170360000","2021","206",1959,2312,4340,4,8,3,1,2,2,"2","6","2","3","2","2","3","1","3","1","2","1","2","1","1","1","73","2","0","0","0","1","2" -"13362270230000","2020","202",1890,756,2750,2,4,1,1,0,1,"2","6","0","3","2","2","3","1","1","1","3","2","3","1","1","1","128","2","0","0","1","2","2" -"09361030150000","2017","203",1928,1357,5280,2,6,1,1,0,1,"2","6","2","3","2","2","1","1","3","1","2","2","2","3","1","1","87","3","0","0","0","2","2" -"17032010190000","2017","211",1887,3299,1204,8,11,3,0,2,3,"2","2","2","3","2","2","7","0","3","1","2","2","2","1","6","2","128","2","0","0","0","1","2" -"13253230040000","2017","203",1969,1040,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","3","1","2","1","2","3","1","1","46","2","0","0","0","2","2" -"17032010190000","2019","211",1888,3299,1204,8,11,3,0,2,3,"2","2","2","3","2","2","7","0","3","1","2","2","2","1","6","2","131","2","0","0","0","1","2" -"09361030150000","2019","203",1928,1357,5280,2,6,1,1,0,1,"2","6","2","3","2","2","1","1","3","1","2","2","2","3","1","1","90","3","0","0","0","2","2" -"09363230550000","2015","203",1923,1200,4375,3,5,1,1,0,1,"2","6","2","3","2","2","7","0","3","1","3","2","2","3","1","1","92","2","0","0","1","2","2" -"09254040180000","2016","203",1955,1571,3750,3,6,1,0,0,5,"2","6","2","1","2","2","7","0","1","1","2","1","2","3","1","1","60","2","0","0","0","1","2" -"13362270230000","2019","202",1890,756,2750,2,4,1,1,0,1,"2","6","0","3","2","2","3","1","1","1","3","2","3","1","1","1","128","2","0","0","1","2","2" -"13253160160000","2018","202",1901,706,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","1","1","2","2","2","3","1","1","117","2","0","0","0","2","2" -"09363230550000","2021","206",1926,2376,4375,4,7,3,1,0,2,"2","6","2","3","2","2","3","1","3","1","3","2","2","3","1","1","98","2","0","0","1","1","2" -"13253230040000","2016","203",1969,1040,3150,2,5,1,0,0,1,"2","6","0","3","2","2","3","1","3","2","2","1","2","3","1","1","46","2","0","0","0","2","2" -"10254170360000","2016","205",1948,1775,4340,4,7,2,1,2,2,"2","6","2","3","2","2","7","0","3","1","2","1","2","1","1","1","67","2","0","0","0","1","2" -"13362270230000","2021","205",1893,1476,2750,4,6,3,1,0,2,"2","6","2","3","2","2","3","1","1","1","3","2","3","1","1","1","131","2","0","1","1","1","2" -"17032010190000","2018","211",1887,3299,1204,8,11,3,0,2,3,"2","2","2","3","2","2","7","0","3","1","2","2","2","1","6","2","131","2","0","0","0","1","2" -"14321260280000","2015","211",1878,2850,3125,6,15,3,0,0,3,"2","2","0","3","2","2","7","0","3","1","2","2","2","3","2","2","137","2","0","0","1","2","2" -"17032010190000","2020","211",1888,3299,1204,8,11,3,0,2,3,"2","2","2","3","2","2","7","0","3","1","2","2","2","1","6","2","131","2","0","0","0","1","2" -"13253160160000","2015","202",1901,706,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","1","1","2","2","2","3","1","1","114","2","0","0","1","2","2" -"09363230550000","2018","203",1923,1200,4375,3,5,1,1,0,1,"2","6","2","3","2","2","7","0","3","1","3","2","2","3","1","1","95","2","0","0","1","2","2" -"09254040180000","2019","203",1955,1571,3750,3,6,1,0,0,5,"2","6","2","1","2","2","7","0","1","1","2","1","2","3","1","1","63","2","0","0","0","1","2" -"10253190450000","2020","204",1951,2469,11160,3,8,2,1,1,1,"2","6","0","3","1","2","3","2","3","3","2","1","2","3","1","1","67","2","0","0","0","1","2" -"09361030150000","2020","203",1928,1357,5280,2,6,1,1,0,1,"2","6","2","3","2","2","1","1","3","1","2","2","2","3","1","1","90","3","0","0","0","2","2" -"13362270230000","2018","202",1890,756,2750,2,4,1,1,0,1,"2","6","0","3","2","2","3","1","1","1","3","2","3","1","1","1","128","2","0","0","1","2","2" -"10253190450000","2015","204",1951,2469,11160,3,8,2,1,1,1,"2","6","0","3","1","2","3","2","3","3","2","1","2","3","1","1","64","2","0","0","0","1","2" -"13253230040000","2018","203",1969,1040,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","3","1","2","1","2","3","1","1","49","2","0","0","0","2","2" -"10253190450000","2016","204",1951,2469,11160,3,8,2,1,1,1,"2","6","0","3","1","2","3","2","3","3","2","1","2","3","1","1","64","2","0","0","0","1","2" -"14321260280000","2019","211",1878,2850,3125,4,9,3,0,0,3,"2","2","2","3","2","2","7","0","3","3","2","1","2","1","2","2","140","2","0","0","0","2","2" -"13253160160000","2016","202",1901,706,3150,2,5,1,0,0,1,"2","6","2","3","2","2","3","1","1","1","2","2","2","3","1","1","114","2","0","0","0","2","2" -"09363230550000","2020","203",1923,1200,4375,3,5,1,1,0,1,"2","6","2","3","2","2","7","0","3","1","3","2","2","3","1","1","95","2","0","0","1","2","2" -"10254170360000","2019","205",1948,1829,4340,3,7,2,1,2,2,"2","6","2","3","2","2","3","1","3","1","2","1","2","1","1","1","70","2","0","0","0","1","2" -"13253230040000","2020","203",1969,1040,3150,2,5,1,0,0,1,"2","6","2","3","2","2","7","0","3","1","2","1","2","3","1","1","49","2","0","0","0","2","2" -"10254170360000","2020","205",1948,1829,4340,3,7,2,1,2,2,"2","6","2","3","2","2","3","1","3","1","2","1","2","1","1","1","70","2","0","0","0","1","2" -"09254040180000","2021","203",1958,1568,3750,3,8,3,0,0,6,"2","6","2","1","2","2","3","1","1","1","2","1","2","1","1","1","66","2","0","0","0","1","2" -"14321260280000","2020","211",1878,2850,3125,4,9,3,0,0,3,"2","2","2","3","2","2","7","0","3","3","2","1","2","1","2","2","140","2","0","0","0","2","2" -"14321260280000","2017","211",1878,2850,3125,4,9,3,0,0,3,"2","2","2","3","2","2","7","0","3","3","2","1","2","1","2","2","137","2","0","0","0","2","2" diff --git a/python/tests/fixtures/chars_sample_athena.csv b/python/tests/fixtures/chars_sample_athena.csv new file mode 120000 index 0000000..6ab79aa --- /dev/null +++ b/python/tests/fixtures/chars_sample_athena.csv @@ -0,0 +1 @@ +../../../data-raw/chars_sample_athena.csv \ No newline at end of file diff --git a/python/tests/fixtures/chars_sample_hie.csv b/python/tests/fixtures/chars_sample_hie.csv deleted file mode 100755 index f618a68..0000000 --- a/python/tests/fixtures/chars_sample_hie.csv +++ /dev/null @@ -1,12 +0,0 @@ -"pin","qu_town","qu_mlt_cd","qu_home_improvement","qu_use","qu_exterior_wall","qu_roof","qu_basement_type","qu_basement_finish","qu_heat","qu_air","qu_attic_type","qu_attic_finish","qu_type_plan","qu_type_design","qu_construct_quality","qu_porch","qu_garage_size","qu_garage_const","qu_garage_attached","qu_garage_area","qu_num_apts","qu_sqft_bld","qu_lnd_sqft","qu_class","qu_rooms","qu_beds","qu_full_bath","qu_half_bath","qu_fire_place","qu_no_com_unit","qu_type_of_res","qu_upload_date","hie_last_year_active","year" -"13253230040000","77","4","1","0","0","0","0","0","0","0","0","0","0","0","0","0","3","1","2","2","0",0,0,"203",0,0,0,0,0,0,"0",2019-06-19,"2023","2019" -"10254170360000","75","3","1","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0",483,0,"206",1,1,1,0,0,0,"0",2017-01-23,"2020","2017" -"13253230040000","77","3","1","0","0","0","1","1","0","1","0","0","0","0","0","0","0","0","0","0","0",1170,0,"278",4,2,2,0,0,0,"2",2017-04-20,"2020","2017" -"09361030150000","71","3","1","0","3","0","0","0","0","1","0","0","0","0","0","0","0","0","0","0","0",1415,0,"206",3,3,2,0,0,0,"2",2017-06-09,"2020","2017" -"13362270230000","77","3","1","0","0","0","0","0","0","1","0","0","0","0","0","0","0","0","0","0","0",720,0,"205",2,2,2,0,0,0,"2",2015-07-01,"2020","2015" -"09254040180000","71","3","1","0","0","0","0","0","0","1","0","0","0","0","0","0","3","1","2","2","0",845,0,"207",2,2,2,0,0,0,"2",2015-08-01,"2020","2015" -"09363230550000","71","3","1","0","0","0","0","0","0","1","0","0","0","0","0","0","3","1","2","2","0",1176,0,"206",2,1,2,0,0,0,"2",2015-08-01,"2020","2015" -"17032010190000","74","3","1","1","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","6",0,0,"210",0,0,0,1,0,0,"0",2015-09-02,"2020","2015" -"10253190450000","75","3","1","0","4","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0",3558,0,"209",4,2,2,0,0,0,"2",2016-02-03,"2020","2016" -"13253160160000","77","3","1","0","0","0","1","1","0","1","0","0","0","0","0","0","0","0","0","0","0",1084,0,"205",2,1,2,0,0,0,"2",2016-08-01,"2020","2016" -"14321260280000","74","3","1","1","0","0","0","0","0","0","0","0","0","0","0","0","4","2","2","2","6",810,0,"206",2,0,2,0,0,0,"0",2016-09-13,"2020","2016" diff --git a/python/tests/fixtures/chars_sample_hie.csv b/python/tests/fixtures/chars_sample_hie.csv new file mode 120000 index 0000000..1340c3f --- /dev/null +++ b/python/tests/fixtures/chars_sample_hie.csv @@ -0,0 +1 @@ +../../../data-raw/chars_sample_hie.csv \ No newline at end of file From 008966f76c924e562a42331a353f1119c28a208d Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 25 Nov 2024 17:23:37 -0600 Subject: [PATCH 38/59] WIP add vars_recode --- python/ccao/vars_funs.py | 132 +++++++++++++++++++++++++++++++++ python/tests/test_vars_funs.py | 89 ++++++++++++++++++++++ 2 files changed, 221 insertions(+) diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index 2a4ddec..876267a 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -126,3 +126,135 @@ def vars_rename( # If the input data is a list, it's not possible to update it inplace, # so ignore that argument return [mapping.get(col, col) for col in data] + + +def vars_recode( + data: pd.DataFrame, + cols: list[str] | None = None, + code_type: str = "long", + as_factor: bool = True, + dictionary: pd.DataFrame | None = None +) -> pd.DataFrame: + """ + Replace numerically coded variables with human-readable values. + + The system of record stores characteristic values in a numerically encoded + format. This function can be used to translate those values into a + human-readable format. For example, EXT_WALL = 2 will become + EXT_WALL = "Frame + Masonry". Note that the values and their translations + must be specified via a user-defined dictionary. The default dictionary is + :data:`vars_dict`. + + :param data: + A pandas DataFrame with columns to have values replaced. + :type data: pandas.DataFrame + + :param cols: + A list of column names to be transformed, or None to select all columns. + :type cols: list[str] + + :param code_type: The recoding type. Options are: + - "long", which transforms EXT_WALL = 1 to EXT_WALL = Frame + - "short", which transforms EXT_WALL = 1 to EXT_WALL = FRME + - "code", which keeps the original values (useful for removing + improperly coded values). + :type code_type: str + + :param as_factor: + If True, re-encoded values will be returned as categorical variables + (pandas Categorical). + If False, re-encoded values will be returned as plain strings. + :type as_factor: bool + + :param dictionary: + A pandas DataFrame representing the dictionary used to translate + encodings. When None, defaults to :data:`vars_dict`. + :type dictionary: pandas.DataFrame + + :raises ValueError: + If the dictionary is missing required columns or if invalid input is + provided. + + :return: + The input DataFrame with re-encoded values for the specified columns. + :rtype: pandas.DataFrame + + .. note:: + Values which are in the data but are NOT in the dictionary will be + converted to NaN. + + :example: + + .. code-block:: python + + import ccao + + sample_data = ccao.sample_athena + + # Defaults to `long` code type + ccao.vars_recode(data=sample_data) + + # Recode to `short` code type + ccao.vars_recode(data=sample_data, code_type="short") + + # Recode only specified columns + ccao.vars_recode(data=sample_data, cols="GAR1_SIZE") + """ + # Validate the dictionary schema + dictionary = dictionary if dictionary is not None else vars_dict + if dictionary.empty: + raise ValueError("dictionary must be a non-empty pandas DataFrame") + + required_columns = {"var_code", "var_value", "var_value_short"} + if not required_columns.issubset(dictionary.columns): + raise ValueError( + "Input dictionary must contain the following columns: " + f"{', '.join(required_columns)}" + ) + + # Validate code type and convert it to the enum + if code_type not in ["short", "long", "code"]: + raise ValueError("code_type must be one of 'short', 'long', or 'code'") + + # Filter the dictionary for categoricals only and and pivot it longer for + # easier lookup + dict_long = dictionary[ + (dictionary["var_type"] == "char") & (dictionary["var_data_type"] == "categorical") + ] + dict_long = dict_long.melt( + id_vars=["var_code", "var_value", "var_value_short"], + value_vars=[col for col in dictionary.columns if col.startswith("var_name_")], + value_name="var_name", + var_name="var_type", + ) + dict_long_pkey = ["var_code", "var_value", "var_value_short", "var_name"] + dict_long = dict_long[dict_long_pkey] + dict_long = dict_long.drop_duplicates(subset=dict_long_pkey) + + # Map the code type to its internal representation in the dictionary + values_to = { + "code": "var_code", + "long": "var_value", + "short": "var_value_short", + }[code_type] + + # Function to apply to each column to remap column values based on the + # vars dict + def transform_column(col: pd.Series, var_name: str, values_to: str, as_factor: bool) -> pd.Series | pd.Categorical: + if var_name in dict_long["var_name"].values: + var_rows = dict_long[dict_long["var_name"] == var_name] + # Get a dictionary mapping the possible codes to their values + var_dict = var_rows.set_index("var_code")[values_to].to_dict() + if as_factor: + return pd.Categorical(col.map(var_dict), categories=list(vars_dict.values())) + else: + return col.map(var_dict) + return col + + # Recode specified columns, or all columns if none were specified + cols = cols or data.columns + for var_name in cols: + if var_name in data.select_dtypes(include=["object", "category"]).columns: + data[var_name] = transform_column(data[var_name], var_name, values_to, as_factor) + + return data diff --git a/python/tests/test_vars_funs.py b/python/tests/test_vars_funs.py index 63c7c93..691bf03 100644 --- a/python/tests/test_vars_funs.py +++ b/python/tests/test_vars_funs.py @@ -122,3 +122,92 @@ def test_vars_rename_invalid_output_type(self): output_type="foo", ) assert "output_type must be one of" in str(exc.value) + + +class TestVarsRecode: + @pytest.fixture( + params=[ + # iasWorld coded data + ("iasworld", pd.DataFrame({ + "pin": ["12345"] * 4, + "extwall": ["1", "2", "0", None], + "bsmt": ["1", "3", "4", "5"], + "value": range(1000, 1004), + "user13": ["1", "2", "4", "3", "0"] + })), + # Athena coded data + ("athena", pd.DataFrame({ + "pin": ["12345"] * 4, + "char_ext_wall": ["1", "2", "0", None], + "char_bsmt": ["1", "3", "4", "5"], + "value": range(1000, 1004), + "char_roof_cnst": ["1", "2", "4", "3", "0"] + })) + ] + ) + def input_data(cls, request): + return request.param + + @pytest.fixture + def expected_output_data(cls): + return { + "short": pd.DataFrame({ + "pin": ["12345"] * 4, + "char_ext_wall": ["FRAM", "MASR", None, None], + "char_bsmt": ["FL", "PT", "CR", None], + "value": range(1000, 1004), + "char_roof_cnst": ["SHAS", "TRGR", "SHKE", "SLTE", None] + }), + "long": pd.DataFrame({ + "pin": ["12345"] * 4, + "char_ext_wall": ["Frame", "Masonry", None, None], + "char_bsmt": ["Full", "Partial", "Crawl", None], + "value": range(1000, 1004), + "char_roof_cnst": ["Shingle + Asphalt", "Tar + Gravel", "Shake", "Slate", None] + }), + "code": pd.DataFrame({ + "pin": ["12345"] * 4, + "char_ext_wall": ["1", "2", "0", None], + "char_bsmt": ["1", "3", "4", "5"], + "value": range(1000, 1004), + "char_roof_cnst": ["1", "2", "4", "3", None] + }) + } + + def _rename_output(self, output, format): + return ccao.vars_rename( + output, names_from="model", names_to=format + ) + + @pytest.mark.parametrize("code_type", ["short", "long", "code"]) + def test_vars_recode_code_type(self, input_data, expected_output_data, code_type): + input_format, input_data = input_data + expected = expected_output_data[code_type] + # Rename the expected output data so it's consistent with whatever input + # data we're looking at + expected_for_code = self._rename_output(expected, input_format) + assert ccao.vars_recode(input_data, code_type=code_type) == expected_for_code + + def test_vars_recode_cols(self, input_data): + input_format, input_data = input_data + expected = expected_output_data["short"] + pytest.fail() + + def test_vars_recode_as_factor(self, input_data): + pytest.fail() + + def test_vars_recode_raises_on_empty_dictionary(self): + with pytest.raises(ValueError) as exc: + ccao.vars_recode(pd.DataFrame(), dictionary=pd.DataFrame()) + assert "non-empty" in str(exc.value) + + def test_vars_recode_raises_on_missing_dictionary_columns(self): + dictionary = ccao.vars_dict.drop(columns=["var_code"]) + with pytest.raises(ValueError) as exc: + ccao.vars_recode(pd.DataFrame(), dictionary=dictionary) + assert "dictionary must contain" in str(exc.value) + + def test_vars_recode_raises_on_invalid_code_type(self): + with pytest.raises(ValueError) as exc: + ccao.vars_recode(pd.DataFrame(), code_type="foo") + assert "code_type must be one of" in str(exc.value) From d132d95118d49fa5ffcc1044f9d15ebb066ef8e5 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Tue, 26 Nov 2024 14:27:59 -0600 Subject: [PATCH 39/59] Add tests for vars_recode and fixup logic --- python/ccao/vars_funs.py | 15 +- python/tests/test_vars_funs.py | 300 ++++++++++++++++++++++++--------- 2 files changed, 231 insertions(+), 84 deletions(-) diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index 4699b79..62ef5c1 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -7,7 +7,7 @@ # Load the default variable dictionary _data_path = importlib.resources.files(ccao.data) -vars_dict = pd.read_csv(str(_data_path / "vars_dict.csv")) +vars_dict = pd.read_csv(str(_data_path / "vars_dict.csv"), dtype=str) # Prefix we use to identify variable name columns in the variable dictionary VAR_NAME_PREFIX = "var_name" @@ -248,11 +248,18 @@ def transform_column( ) -> pd.Series | pd.Categorical: if var_name in dict_long["var_name"].values: var_rows = dict_long[dict_long["var_name"] == var_name] - # Get a dictionary mapping the possible codes to their values - var_dict = var_rows.set_index("var_code")[values_to].to_dict() + # Get a dictionary mapping the possible codes to their values. + # Use `var_code` as the index (keys) for the dictionary, unless + # we're selecting `var_code`, in which case we can't set it as the + # index and use it for values + var_dict = ( + {code: code for code in var_rows["var_code"].tolist()} + if values_to == "var_code" + else var_rows.copy().set_index("var_code")[values_to].to_dict() + ) if as_factor: return pd.Categorical( - col.map(var_dict), categories=list(vars_dict.values()) + col.map(var_dict), categories=list(var_dict.values()) ) else: return col.map(var_dict) diff --git a/python/tests/test_vars_funs.py b/python/tests/test_vars_funs.py index 05b7824..34ffaee 100644 --- a/python/tests/test_vars_funs.py +++ b/python/tests/test_vars_funs.py @@ -1,3 +1,4 @@ +import numpy as np import pandas as pd import pytest @@ -125,99 +126,238 @@ def test_vars_rename_invalid_output_type(self): class TestVarsRecode: - @pytest.fixture( - params=[ - # iasWorld coded data - ( - "iasworld", - pd.DataFrame( - { - "pin": ["12345"] * 4, - "extwall": ["1", "2", "0", None], - "bsmt": ["1", "3", "4", "5"], - "value": range(1000, 1004), - "user13": ["1", "2", "4", "3", "0"], - } - ), - ), - # Athena coded data - ( - "athena", - pd.DataFrame( - { - "pin": ["12345"] * 4, - "char_ext_wall": ["1", "2", "0", None], - "char_bsmt": ["1", "3", "4", "5"], - "value": range(1000, 1004), - "char_roof_cnst": ["1", "2", "4", "3", "0"], - } - ), - ), + @pytest.fixture(scope="class") + def raw_columns(cls) -> list[dict]: + """Metadata describing the columns that we use as fixtures for all + vars_recode tests. Each element of the list is a dict representing a + column""" + return [ + { + # Structure of the input column. We parameterize input data in + # the `input_data` fixture with one parameter per element of + # this dict + "input": { + "athena": {"name": "pin", "value": ["12345"] * 4}, + "iasworld": {"name": "pin", "value": ["12345"] * 4}, + }, + # Structure of the output column. We select the proper column + # based on key for different types of tests + "expected": { + "short": {"name": "pin", "value": ["12345"] * 4}, + "long": {"name": "pin", "value": ["12345"] * 4}, + "code": {"name": "pin", "value": ["12345"] * 4}, + "factor": {"name": "pin", "value": ["12345"] * 4}, + # If `value` is True, expect the column value to be + # recoded to the "long" format; otherwise, expect the + # column value to stay the same as the input value + "col": {"name": "pin", "value": False}, + }, + }, + { + "input": { + "athena": { + "name": "char_ext_wall", + "value": ["1", "2", "0", None], + }, + "iasworld": { + "name": "extwall", + "value": ["1", "2", "0", None], + }, + }, + "expected": { + "short": { + "name": "char_ext_wall", + "value": ["FRAM", "MASR", np.nan, np.nan], + }, + "long": { + "name": "char_ext_wall", + "value": ["Frame", "Masonry", np.nan, np.nan], + }, + "code": { + "name": "char_ext_wall", + "value": ["1", "2", np.nan, np.nan], + }, + "factor": { + "name": "char_ext_wall", + "value": pd.Categorical( + ["1", "2", np.nan, np.nan], + categories=["1", "2", "3", "4"], + ), + }, + "col": {"name": "char_ext_wall", "value": True}, + }, + }, + { + "input": { + "athena": { + "name": "char_bsmt", + "value": ["1", "3", "4", "5"], + }, + "iasworld": { + "name": "bsmt", + "value": ["1", "3", "4", "5"], + }, + }, + "expected": { + "short": { + "name": "char_bsmt", + "value": ["FL", "PT", "CR", np.nan], + }, + "long": { + "name": "char_bsmt", + "value": ["Full", "Partial", "Crawl", np.nan], + }, + "code": { + "name": "char_bsmt", + "value": ["1", "3", "4", np.nan], + }, + "factor": { + "name": "char_bsmt", + "value": pd.Categorical( + ["1", "3", "4", np.nan], + categories=["1", "2", "3", "4"], + ), + }, + "col": {"name": "char_bsmt", "value": True}, + }, + }, + { + "input": { + "athena": {"name": "value", "value": range(1000, 1004)}, + "iasworld": {"name": "value", "value": range(1000, 1004)}, + }, + "expected": { + "short": {"name": "value", "value": range(1000, 1004)}, + "long": {"name": "value", "value": range(1000, 1004)}, + "code": {"name": "value", "value": range(1000, 1004)}, + "factor": {"name": "value", "value": range(1000, 1004)}, + "col": {"name": "value", "value": False}, + }, + }, + { + "input": { + "athena": { + "name": "char_roof_cnst", + "value": ["1", "2", "3", "0"], + }, + "iasworld": { + "name": "user13", + "value": ["1", "2", "3", "0"], + }, + }, + "expected": { + "short": { + "name": "char_roof_cnst", + "value": ["SHAS", "TRGR", "SLTE", np.nan], + }, + "long": { + "name": "char_roof_cnst", + "value": [ + "Shingle + Asphalt", + "Tar + Gravel", + "Slate", + np.nan, + ], + }, + "code": { + "name": "char_roof_cnst", + "value": ["1", "2", "3", np.nan], + }, + "factor": { + "name": "char_roof_cnst", + "value": pd.Categorical( + ["1", "2", "3", np.nan], + categories=["1", "2", "3", "4", "5", "6"], + ), + }, + "col": {"name": "char_roof_cnst", "value": False}, + }, + }, ] - ) - def input_data(cls, request): - return request.param - @pytest.fixture - def expected_output_data(cls): - return { - "short": pd.DataFrame( - { - "pin": ["12345"] * 4, - "char_ext_wall": ["FRAM", "MASR", None, None], - "char_bsmt": ["FL", "PT", "CR", None], - "value": range(1000, 1004), - "char_roof_cnst": ["SHAS", "TRGR", "SHKE", "SLTE", None], - } - ), - "long": pd.DataFrame( - { - "pin": ["12345"] * 4, - "char_ext_wall": ["Frame", "Masonry", None, None], - "char_bsmt": ["Full", "Partial", "Crawl", None], - "value": range(1000, 1004), - "char_roof_cnst": [ - "Shingle + Asphalt", - "Tar + Gravel", - "Shake", - "Slate", - None, - ], - } - ), - "code": pd.DataFrame( + @pytest.fixture(params=["athena", "iasworld"]) + def input_data(cls, request, raw_columns): + input_type = request.param + return ( + input_type, + pd.DataFrame( { - "pin": ["12345"] * 4, - "char_ext_wall": ["1", "2", "0", None], - "char_bsmt": ["1", "3", "4", "5"], - "value": range(1000, 1004), - "char_roof_cnst": ["1", "2", "4", "3", None], + col["input"][input_type]["name"]: col["input"][input_type][ + "value" + ] + for col in raw_columns } ), - } - - def _rename_output(self, output, format): - return ccao.vars_rename(output, names_from="model", names_to=format) + ) @pytest.mark.parametrize("code_type", ["short", "long", "code"]) - def test_vars_recode_code_type( - self, input_data, expected_output_data, code_type - ): + def test_vars_recode_code_type(self, input_data, raw_columns, code_type): input_format, input_data = input_data - expected = expected_output_data[code_type] + expected_output = pd.DataFrame( + { + col["expected"][code_type]["name"]: col["expected"][code_type][ + "value" + ] + for col in raw_columns + } + ) # Rename the expected output data so it's consistent with whatever input # data we're looking at - expected_for_code = self._rename_output(expected, input_format) - assert ( - ccao.vars_recode(input_data, code_type=code_type) - == expected_for_code + expected_renamed = ccao.vars_rename( + expected_output, names_from="model", names_to=input_format ) + recoded = ccao.vars_recode( + input_data, code_type=code_type, as_factor=False + ) + assert recoded.equals(expected_renamed) - def test_vars_recode_cols(self, input_data): - pytest.fail() + def test_vars_recode_cols(self, input_data, raw_columns): + input_format, input_data = input_data + cols = [ + col["expected"]["col"]["name"] + for col in raw_columns + if col["expected"]["col"]["value"] is True + ] + # Rename the cols so they match the input data schema + cols = ccao.vars_rename( + cols, names_from="model", names_to=input_format + ) + code_type = "short" + expected_output = pd.DataFrame( + { + col["expected"]["col"]["name"]: ( + col["expected"][code_type]["value"] + if col["expected"]["col"]["value"] is True + else col["input"]["athena"]["value"] + ) + for col in raw_columns + } + ) + expected_renamed = ccao.vars_rename( + expected_output, names_from="model", names_to=input_format + ) + recoded = ccao.vars_recode( + input_data, cols=cols, code_type=code_type, as_factor=False + ) + assert recoded.equals(expected_renamed) - def test_vars_recode_as_factor(self, input_data): - pytest.fail() + def test_vars_recode_as_factor(self, input_data, raw_columns): + input_format, input_data = input_data + expected_output = pd.DataFrame( + { + col["expected"]["factor"]["name"]: col["expected"]["factor"][ + "value" + ] + for col in raw_columns + } + ) + expected_renamed = ccao.vars_rename( + expected_output, names_from="model", names_to=input_format + ) + recoded = ccao.vars_recode( + input_data, code_type="code", as_factor=True + ) + assert recoded.equals(expected_renamed) def test_vars_recode_raises_on_empty_dictionary(self): with pytest.raises(ValueError) as exc: From 204cccf2052429f1de9d39f390e048d13ffbe5ed Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Tue, 26 Nov 2024 16:52:41 -0600 Subject: [PATCH 40/59] Add docs for vars_dict and vars_recode in Python package --- python/ccao/vars_funs.py | 18 +++++++++++------- python/docs/source/reference.rst | 17 +++++++++++++++-- python/docs/source/vars_dict.rst | 28 ++++++++++++++++++++++++++++ python/docs/source/vars_recode.rst | 5 +++++ 4 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 python/docs/source/vars_dict.rst create mode 100644 python/docs/source/vars_recode.rst diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index 62ef5c1..dda6c9a 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -145,19 +145,23 @@ def vars_recode( must be specified via a user-defined dictionary. The default dictionary is :data:`vars_dict`. + Options for ``code_type`` are: + + - ``"long"``, which transforms EXT_WALL = 1 to EXT_WALL = Frame + - ``"short"``, which transforms EXT_WALL = 1 to EXT_WALL = FRME + - ``"code"``, which keeps the original values (useful for removing + improperly coded values, see the note below) + :param data: A pandas DataFrame with columns to have values replaced. :type data: pandas.DataFrame :param cols: - A list of column names to be transformed, or None to select all columns. + A list of column names to be transformed, or ``None`` to select all columns. :type cols: list[str] - :param code_type: The recoding type. Options are: - - "long", which transforms EXT_WALL = 1 to EXT_WALL = Frame - - "short", which transforms EXT_WALL = 1 to EXT_WALL = FRME - - "code", which keeps the original values (useful for removing - improperly coded values). + :param code_type: + The recoding type. See description above for options. :type code_type: str :param as_factor: @@ -168,7 +172,7 @@ def vars_recode( :param dictionary: A pandas DataFrame representing the dictionary used to translate - encodings. When None, defaults to :data:`vars_dict`. + encodings. :type dictionary: pandas.DataFrame :raises ValueError: diff --git a/python/docs/source/reference.rst b/python/docs/source/reference.rst index 3dc531e..07902f3 100644 --- a/python/docs/source/reference.rst +++ b/python/docs/source/reference.rst @@ -9,6 +9,19 @@ Manage characteristics ^^^^^^^^^^^^^^^^^^^^^^ Recode/rename characteristic columns, merge HIE data, and fix characteristic -errors. +errors -:doc:`vars_rename() ` +:doc:`vars_rename() ` |nbsp| +:doc:`vars_recode() ` + +Data +---- + +Dictionaries +^^^^^^^^^^^^ + +Lookups for numeric codes used in the assessment system + +:doc:`vars_dict ` + +.. |nbsp| unicode:: 0xA0 diff --git a/python/docs/source/vars_dict.rst b/python/docs/source/vars_dict.rst new file mode 100644 index 0000000..c085f8c --- /dev/null +++ b/python/docs/source/vars_dict.rst @@ -0,0 +1,28 @@ +================================================ +Data dictionary for CCAO data sets and variables +================================================ + +A crosswalk of CCAO variable names used in iasWorld, AWS, modeling, +and open data. Also includes a translation of numeric character codes +to their human-readable value (ROOF_CNST = 1 +becomes ROOF_CNST = Shingle/Asphalt). + +Format +------ + +A pandas DataFrame with the following columns: + +- **var_name_hie**: Column name of variable when stored in the legacy ADDCHARS SQL table. +- **var_name_iasworld**: Column name for variable as stored in the system of record (iasWorld). +- **var_name_athena**: Column name used for views and tables in AWS Athena. +- **var_name_model**: Column name used while data is flowing through modeling pipelines. +- **var_name_publish**: Human-readable column name used for public data sets. +- **var_name_pretty**: Human-readable column name used for publication and reporting. +- **var_type**: Variable type/prefix indicating the variable's function. For example, + ``ind_`` variables are always indicators (booleans), while ``char_`` variables are + always property characteristics. +- **var_data_type**: R data type variable values should be stored as. +- **var_code**: Factor value for categorical variables. These are the values stored + in the system of record. +- **var_value**: Human-readable translation of factor value. +- **var_value_short**: Human-readable translation of factor value, but as short as possible. diff --git a/python/docs/source/vars_recode.rst b/python/docs/source/vars_recode.rst new file mode 100644 index 0000000..6bdc9d3 --- /dev/null +++ b/python/docs/source/vars_recode.rst @@ -0,0 +1,5 @@ +============================================================== +Replace numerically coded variables with human-readable values +============================================================== + +.. autofunction:: ccao.vars_recode From 31dffc37b1ff099a85703a54c235d5c654991cfc Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Tue, 26 Nov 2024 17:10:37 -0600 Subject: [PATCH 41/59] Remove unnecessary select_dtypes filter in Python vars_recode --- python/ccao/vars_funs.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index dda6c9a..892ae29 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -272,10 +272,7 @@ def transform_column( # Recode specified columns, or all columns if none were specified cols = cols or data.columns for var_name in cols: - if ( - var_name - in data.select_dtypes(include=["object", "category"]).columns - ): + if var_name in data.columns: data[var_name] = transform_column( data[var_name], var_name, values_to, as_factor ) From a8c3233779bdb8ade18fe4e141315c8d65f3c709 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 27 Nov 2024 18:29:38 +0000 Subject: [PATCH 42/59] Add python/ subdir to RBuildignore so it does not get built into R package --- .Rbuildignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.Rbuildignore b/.Rbuildignore index be1d40b..82b45ad 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -26,6 +26,7 @@ ^man-roxygen$ ^pkgdown$ ^public$ +^python ^renv$ ^renv\.lock$ ^vignettes$ From 750597032c3d8b83ad1668574b3779178ff2b696 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 27 Nov 2024 20:33:36 +0000 Subject: [PATCH 43/59] Support Python 3.9, pandas 1.4, and numpy 1.23 --- .github/workflows/pytest-coverage.yaml | 37 +++++++++++++++++++++----- python/ccao/vars_funs.py | 13 ++++----- python/pyproject.toml | 20 +++++++------- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/.github/workflows/pytest-coverage.yaml b/.github/workflows/pytest-coverage.yaml index e758284..554ec3d 100644 --- a/.github/workflows/pytest-coverage.yaml +++ b/.github/workflows/pytest-coverage.yaml @@ -15,7 +15,24 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.10", "3.11", "3.12", "3.13"] + version: + # Test the lower bound of pandas/numpy support for Python 3.9, + # and test the upper bound for all other Python versions + - python: "3.9" + pandas: "1.4.*" + numpy: "1.23.*" + - python: "3.10" + pandas: "2.2.*" + numpy: "2.1.*" + - python: "3.11" + pandas: "2.2.*" + numpy: "2.1.*" + - python: "3.12" + pandas: "2.2.*" + numpy: "2.1.*" + - python: "3.13" + pandas: "2.2.*" + numpy: "2.1.*" steps: - name: Checkout code uses: actions/checkout@v4 @@ -27,26 +44,34 @@ jobs: cache-dependency-glob: python/pyproject.toml cache-suffix: test - - name: Install Python ${{ matrix.python-version }} + - name: Install Python ${{ matrix.version.python }} uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.version.python }} - name: Install Python dependencies working-directory: python shell: bash run: uv pip install .[dev,docs] + - name: Install pandas ${{ matrix.version.pandas }} and numpy ${{ matrix.version.numpy }} + working-directory: python + shell: bash + run: | + uv pip install \ + pandas==${{ matrix.version.pandas }} \ + numpy==${{ matrix.version.numpy}} + - name: Run pytest working-directory: python shell: bash run: | pytest -v --doctest-modules \ - --junitxml=junit/test-results-${{ matrix.python-version }}.xmlpytest + --junitxml=junit/test-results-${{ matrix.version.python }}.xmlpytest - name: Upload artifacts if: failure() uses: actions/upload-artifact@v4 with: - name: python/pytest-results-${{ matrix.python-version }} - path: python/junit/test-results-${{ matrix.python-version }}.xml + name: python/pytest-results-${{ matrix.version.python }} + path: python/junit/test-results-${{ matrix.version.python }}.xml diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index 892ae29..5afed5a 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -1,5 +1,6 @@ # Functions for translating variables between different data sources import importlib.resources +import typing import pandas as pd @@ -14,12 +15,12 @@ def vars_rename( - data: list[str] | pd.DataFrame, + data: typing.Union[typing.List[str], pd.DataFrame], names_from: str, names_to: str, output_type: str = "inplace", - dictionary: pd.DataFrame | None = None, -) -> list[str] | pd.DataFrame: + dictionary: typing.Optional[pd.DataFrame] = None, +) -> typing.Union[typing.List[str], pd.DataFrame]: """ Rename variables from one naming convention to another. @@ -130,10 +131,10 @@ def vars_rename( def vars_recode( data: pd.DataFrame, - cols: list[str] | None = None, + cols: typing.Optional[typing.List[str]] = None, code_type: str = "long", as_factor: bool = True, - dictionary: pd.DataFrame | None = None, + dictionary: typing.Optional[pd.DataFrame] = None, ) -> pd.DataFrame: """ Replace numerically coded variables with human-readable values. @@ -249,7 +250,7 @@ def vars_recode( # vars dict def transform_column( col: pd.Series, var_name: str, values_to: str, as_factor: bool - ) -> pd.Series | pd.Categorical: + ) -> typing.Union[pd.Series, pd.Categorical]: if var_name in dict_long["var_name"].values: var_rows = dict_long[dict_long["var_name"] == var_name] # Get a dictionary mapping the possible codes to their values. diff --git a/python/pyproject.toml b/python/pyproject.toml index 733619b..6f50f47 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -3,26 +3,28 @@ name = "ccao" version = "1.3.0" description = "Convenience Functions and Datasets for the Cook County Assessor's Office" readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.9" authors = [ {name = "Jean Cochrane", email="jean.cochrane@cookcountyil.gov"}, {name = "Dan Snow", email="daniel.snow@cookcountyil.gov"}, ] dependencies = [ - "pandas>=2.2.3", + "pandas>=1.4.0,<=2.3.0", + "numpy>=1.23.0,<=2.2.0" ] [project.optional-dependencies] dev = [ - "mypy>=1.13.0", - "pytest>=8.3.3", - "ruff>=0.7.4", + "mypy", + "pytest", + "ruff", ] docs = [ - "Sphinx>=8.1.3", - "myst-parser>=4.0.0", - "pydata-sphinx-theme>=0.16.0", - "sphinx-pyproject>=0.3.0" + "Sphinx", + "myst-parser", + "pydata-sphinx-theme", + "sphinx-pyproject", + "sphinx-autobuild" ] [tool.setuptools] From df3af62a7fadae5956661d7a21272cefa7407db1 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 27 Nov 2024 20:39:21 +0000 Subject: [PATCH 44/59] Try installing pandas/numpy before the other dependencies in pytest-coverage workflow --- .github/workflows/pytest-coverage.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pytest-coverage.yaml b/.github/workflows/pytest-coverage.yaml index 554ec3d..d3a6fce 100644 --- a/.github/workflows/pytest-coverage.yaml +++ b/.github/workflows/pytest-coverage.yaml @@ -49,11 +49,6 @@ jobs: with: python-version: ${{ matrix.version.python }} - - name: Install Python dependencies - working-directory: python - shell: bash - run: uv pip install .[dev,docs] - - name: Install pandas ${{ matrix.version.pandas }} and numpy ${{ matrix.version.numpy }} working-directory: python shell: bash @@ -62,6 +57,11 @@ jobs: pandas==${{ matrix.version.pandas }} \ numpy==${{ matrix.version.numpy}} + - name: Install Python dependencies + working-directory: python + shell: bash + run: uv pip install .[dev,docs] + - name: Run pytest working-directory: python shell: bash From e34b6d7856636d0cbbcee4f5e7a95db7c7cab417 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 2 Dec 2024 18:03:34 +0000 Subject: [PATCH 45/59] Try building and testing Python package with tox --- .github/workflows/pytest-coverage.yaml | 77 -------------------- .github/workflows/python-build-and-test.yaml | 44 +++++++++++ python/pyproject.toml | 52 ++++++++++--- 3 files changed, 86 insertions(+), 87 deletions(-) delete mode 100644 .github/workflows/pytest-coverage.yaml create mode 100644 .github/workflows/python-build-and-test.yaml diff --git a/.github/workflows/pytest-coverage.yaml b/.github/workflows/pytest-coverage.yaml deleted file mode 100644 index d3a6fce..0000000 --- a/.github/workflows/pytest-coverage.yaml +++ /dev/null @@ -1,77 +0,0 @@ -on: - pull_request: - push: - branches: [main, master] - -name: pytest-coverage - -env: - PYTHONUNBUFFERED: "1" - UV_SYSTEM_PYTHON: 1 - -jobs: - pytest-coverage: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - version: - # Test the lower bound of pandas/numpy support for Python 3.9, - # and test the upper bound for all other Python versions - - python: "3.9" - pandas: "1.4.*" - numpy: "1.23.*" - - python: "3.10" - pandas: "2.2.*" - numpy: "2.1.*" - - python: "3.11" - pandas: "2.2.*" - numpy: "2.1.*" - - python: "3.12" - pandas: "2.2.*" - numpy: "2.1.*" - - python: "3.13" - pandas: "2.2.*" - numpy: "2.1.*" - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install uv - uses: astral-sh/setup-uv@v3 - with: - enable-cache: true - cache-dependency-glob: python/pyproject.toml - cache-suffix: test - - - name: Install Python ${{ matrix.version.python }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.version.python }} - - - name: Install pandas ${{ matrix.version.pandas }} and numpy ${{ matrix.version.numpy }} - working-directory: python - shell: bash - run: | - uv pip install \ - pandas==${{ matrix.version.pandas }} \ - numpy==${{ matrix.version.numpy}} - - - name: Install Python dependencies - working-directory: python - shell: bash - run: uv pip install .[dev,docs] - - - name: Run pytest - working-directory: python - shell: bash - run: | - pytest -v --doctest-modules \ - --junitxml=junit/test-results-${{ matrix.version.python }}.xmlpytest - - - name: Upload artifacts - if: failure() - uses: actions/upload-artifact@v4 - with: - name: python/pytest-results-${{ matrix.version.python }} - path: python/junit/test-results-${{ matrix.version.python }}.xml diff --git a/.github/workflows/python-build-and-test.yaml b/.github/workflows/python-build-and-test.yaml new file mode 100644 index 0000000..e7f483c --- /dev/null +++ b/.github/workflows/python-build-and-test.yaml @@ -0,0 +1,44 @@ +on: + pull_request: + push: + branches: [main, master] + +name: python-build-and-test + +env: + PYTHONUNBUFFERED: "1" + +jobs: + build-and-test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: python/pyproject.toml + cache-suffix: test + + - name: Install Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install tox + shell: bash + run: | + uv tool install tox --with tox-uv,tox-gh-actions + tox --version + + - name: Build and test with tox + shell: bash + working-directory: python + run: tox r diff --git a/python/pyproject.toml b/python/pyproject.toml index 6f50f47..d24e369 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -9,22 +9,22 @@ authors = [ {name = "Dan Snow", email="daniel.snow@cookcountyil.gov"}, ] dependencies = [ - "pandas>=1.4.0,<=2.3.0", - "numpy>=1.23.0,<=2.2.0" + "pandas>=1.4.3", + "numpy>=1.23.1" ] [project.optional-dependencies] dev = [ - "mypy", - "pytest", - "ruff", + "mypy>=1.0.0", + "pytest>=7.0.0", + "ruff>=0.8.0", ] docs = [ - "Sphinx", - "myst-parser", - "pydata-sphinx-theme", - "sphinx-pyproject", - "sphinx-autobuild" + "Sphinx>=7.0", + "myst-parser>=1.0.0", + "pydata-sphinx-theme>=0.16.0", + "sphinx-pyproject>=0.3.0", + "sphinx-autobuild>=2024.10.3" ] [tool.setuptools] @@ -57,3 +57,35 @@ highlight_language = "none" html_theme = "pydata_sphinx_theme" html_logo = "../images/logo.png" html_show_copyright = false + +[tool.pytest.ini_options] +minversion = "7.0" +addopts = "-v --cache-clear -rf --doctest-modules" +console_output_style = "count" + +[tool.tox] +legacy_tox_ini = """ +[tox] +min_version = 4.0 +envlist = + py{39, 310, 311}-lowest + py{39, 310, 311, 312, 313} + +[gh-actions] +python = + 3.9: py39 + 3.10: py310 + 3.11: py311 + 3.12: py312 + 3.13: py313 + +[testenv] +extras = dev,docs +commands = pytest + +[testenv:py{39, 310, 311}-lowest] +uv_resolution = lowest-direct + +[testenv:py{310, 311, 312, 313}] +uv_resolution = highest +""" From 56371586b18cb0c3e79ba1d3be4c254f2ac1f817 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 2 Dec 2024 19:47:27 +0000 Subject: [PATCH 46/59] Add UV_CACHE_DIR to tox env to see if it speeds up builds --- python/pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/pyproject.toml b/python/pyproject.toml index d24e369..1a5c766 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -82,6 +82,8 @@ python = [testenv] extras = dev,docs commands = pytest +passenv = + UV_CACHE_DIR [testenv:py{39, 310, 311}-lowest] uv_resolution = lowest-direct From 0e29e6feab426c35bb5d9ccf0a67eb1ada136422 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 2 Dec 2024 20:03:07 +0000 Subject: [PATCH 47/59] Revert "Add UV_CACHE_DIR to tox env to see if it speeds up builds" This reverts commit 56371586b18cb0c3e79ba1d3be4c254f2ac1f817. --- python/pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/pyproject.toml b/python/pyproject.toml index 1a5c766..d24e369 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -82,8 +82,6 @@ python = [testenv] extras = dev,docs commands = pytest -passenv = - UV_CACHE_DIR [testenv:py{39, 310, 311}-lowest] uv_resolution = lowest-direct From b410f6e028975666e03a82d9271628b67ba70e94 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 2 Dec 2024 20:03:56 +0000 Subject: [PATCH 48/59] Restrict tox envs since 3.11 seems to need to build a dep from source --- python/pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/pyproject.toml b/python/pyproject.toml index d24e369..2f70d09 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -68,7 +68,7 @@ legacy_tox_ini = """ [tox] min_version = 4.0 envlist = - py{39, 310, 311}-lowest + py{39, 310}-lowest py{39, 310, 311, 312, 313} [gh-actions] @@ -83,9 +83,9 @@ python = extras = dev,docs commands = pytest -[testenv:py{39, 310, 311}-lowest] +[testenv:py{39, 310}-lowest] uv_resolution = lowest-direct -[testenv:py{310, 311, 312, 313}] +[testenv:py{39, 310, 311, 312, 313}] uv_resolution = highest """ From d135b9a103dc372c3cd603bb085757771d183428 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 2 Dec 2024 21:02:10 +0000 Subject: [PATCH 49/59] Update docs to fix incorrect EXT_WALL code translation --- R/vars_funs.R | 2 +- python/ccao/vars_funs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/vars_funs.R b/R/vars_funs.R index d8862be..62506a0 100644 --- a/R/vars_funs.R +++ b/R/vars_funs.R @@ -200,7 +200,7 @@ vars_rename <- function(data, #' @description The system of record stores characteristic values in a #' numerically encoded format. This function can be used to translate those #' values into a human-readable format. For example, EXT_WALL = 2 will become -#' EXT_WALL = "Frame + Masonry". Note that the values and their translations are +#' EXT_WALL = "Masonry". Note that the values and their translations are #' must be specified via a user-defined dictionary. The default dictionary is #' \code{\link{vars_dict}}. #' diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index 892ae29..2c3bbaa 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -141,7 +141,7 @@ def vars_recode( The system of record stores characteristic values in a numerically encoded format. This function can be used to translate those values into a human-readable format. For example, EXT_WALL = 2 will become - EXT_WALL = "Frame + Masonry". Note that the values and their translations + EXT_WALL = "Masonry". Note that the values and their translations must be specified via a user-defined dictionary. The default dictionary is :data:`vars_dict`. From 2a82c96791830b07b1460e7b66df6df3c728df03 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 2 Dec 2024 21:11:49 +0000 Subject: [PATCH 50/59] Clarify docs for vars_dict data object in reference.rst --- python/docs/source/reference.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/docs/source/reference.rst b/python/docs/source/reference.rst index 07902f3..c391428 100644 --- a/python/docs/source/reference.rst +++ b/python/docs/source/reference.rst @@ -20,7 +20,7 @@ Data Dictionaries ^^^^^^^^^^^^ -Lookups for numeric codes used in the assessment system +Lookups for numeric codes and variable names used in the assessment system :doc:`vars_dict ` From f5ee577a4ba516eb23e9b45a0e244a39af79c7c3 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 2 Dec 2024 21:12:22 +0000 Subject: [PATCH 51/59] Stricter dictionary schema validation in Python version of vars_recode --- python/ccao/vars_funs.py | 13 ++++++++++++- python/tests/test_vars_funs.py | 10 +++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index 2c3bbaa..1d1d697 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -209,13 +209,24 @@ def vars_recode( if dictionary.empty: raise ValueError("dictionary must be a non-empty pandas DataFrame") - required_columns = {"var_code", "var_value", "var_value_short"} + required_columns = { + "var_code", + "var_value", + "var_value_short", + "var_type", + "var_data_type", + } if not required_columns.issubset(dictionary.columns): raise ValueError( "Input dictionary must contain the following columns: " f"{', '.join(required_columns)}" ) + if not any(col.startswith("var_name_") for col in dictionary.columns): + raise ValueError( + "Input dictionary must contain at least one var_name_ column" + ) + # Validate code type and convert it to the enum if code_type not in ["short", "long", "code"]: raise ValueError("code_type must be one of 'short', 'long', or 'code'") diff --git a/python/tests/test_vars_funs.py b/python/tests/test_vars_funs.py index 34ffaee..b1341f5 100644 --- a/python/tests/test_vars_funs.py +++ b/python/tests/test_vars_funs.py @@ -368,7 +368,15 @@ def test_vars_recode_raises_on_missing_dictionary_columns(self): dictionary = ccao.vars_dict.drop(columns=["var_code"]) with pytest.raises(ValueError) as exc: ccao.vars_recode(pd.DataFrame(), dictionary=dictionary) - assert "dictionary must contain" in str(exc.value) + assert "dictionary must contain the following column" in str(exc.value) + + def test_vars_recode_raises_on_missing_var_name_columns(self): + dictionary = ccao.vars_dict.drop( + columns=list(ccao.vars_dict.filter(regex="var_name_")) + ) + with pytest.raises(ValueError) as exc: + ccao.vars_recode(pd.DataFrame(), dictionary=dictionary) + assert "dictionary must contain at least one" in str(exc.value) def test_vars_recode_raises_on_invalid_code_type(self): with pytest.raises(ValueError) as exc: From 67ea0bb187d02893f0344abda561ce171f5af838 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 2 Dec 2024 15:16:41 -0600 Subject: [PATCH 52/59] Remove outdated comment in python/ccao/vars_funs.py Co-authored-by: Dan Snow <31494343+dfsnow@users.noreply.github.com> --- python/ccao/vars_funs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/ccao/vars_funs.py b/python/ccao/vars_funs.py index 1d1d697..644cbee 100644 --- a/python/ccao/vars_funs.py +++ b/python/ccao/vars_funs.py @@ -227,7 +227,6 @@ def vars_recode( "Input dictionary must contain at least one var_name_ column" ) - # Validate code type and convert it to the enum if code_type not in ["short", "long", "code"]: raise ValueError("code_type must be one of 'short', 'long', or 'code'") From b9f300cd025d74cbee42f1617e038811294a20dc Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 2 Dec 2024 22:55:03 +0000 Subject: [PATCH 53/59] Fix wheel caching on CI when using uv in Python package --- .github/workflows/python-build-and-test.yaml | 4 ++-- python/pyproject.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-build-and-test.yaml b/.github/workflows/python-build-and-test.yaml index e7f483c..fd338ec 100644 --- a/.github/workflows/python-build-and-test.yaml +++ b/.github/workflows/python-build-and-test.yaml @@ -21,11 +21,11 @@ jobs: uses: actions/checkout@v4 - name: Install uv - uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@v4 with: enable-cache: true cache-dependency-glob: python/pyproject.toml - cache-suffix: test + cache-suffix: ${{ matrix.python-version }}-test - name: Install Python ${{ matrix.python-version }} uses: actions/setup-python@v5 diff --git a/python/pyproject.toml b/python/pyproject.toml index 2f70d09..ab21060 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -68,7 +68,7 @@ legacy_tox_ini = """ [tox] min_version = 4.0 envlist = - py{39, 310}-lowest + py{39, 310, 311}-lowest py{39, 310, 311, 312, 313} [gh-actions] @@ -83,7 +83,7 @@ python = extras = dev,docs commands = pytest -[testenv:py{39, 310}-lowest] +[testenv:py{39, 310, 311}-lowest] uv_resolution = lowest-direct [testenv:py{39, 310, 311, 312, 313}] From 815b6a828763bbadf3b939ca483502361e45fd45 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 2 Dec 2024 22:56:48 +0000 Subject: [PATCH 54/59] Speed up Python install with uv in docs.yaml --- .github/workflows/docs.yaml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index d1b8bfd..bcf02a3 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -33,16 +33,12 @@ jobs: needs: website - name: Install uv - uses: astral-sh/setup-uv@v3 - with: - enable-cache: true - cache-dependency-glob: python/pyproject.toml - cache-suffix: docs + uses: astral-sh/setup-uv@v4 - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version-file: python/pyproject.toml + shell: bash + working-directory: python + run: uv venv - name: Install Python dependencies working-directory: python @@ -54,7 +50,7 @@ jobs: shell: Rscript {0} - name: Build Python docs - run: sphinx-build python/docs/source docs/python + run: uv run sphinx-build python/docs/source docs/python - name: Configure pages uses: actions/configure-pages@v5 From 0890d98b349475d6b9389ad88156463897a276b9 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Mon, 2 Dec 2024 23:11:50 +0000 Subject: [PATCH 55/59] Pass env vars to tox defensively --- python/pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/pyproject.toml b/python/pyproject.toml index ab21060..2f3a81f 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -82,6 +82,9 @@ python = [testenv] extras = dev,docs commands = pytest +passenv = + UV_CACHE_DIR + PYTHONUNBUFFERED [testenv:py{39, 310, 311}-lowest] uv_resolution = lowest-direct From dcf038f0714bfa2e0eb2d178619b19b84a1ab048 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 4 Dec 2024 15:52:24 -0600 Subject: [PATCH 56/59] Remove UV_SYSTEM_PYTHON env var from docs workflow --- .github/workflows/docs.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index bcf02a3..1bdd911 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -9,7 +9,6 @@ name: docs env: PYTHONUNBUFFERED: "1" - UV_SYSTEM_PYTHON: 1 jobs: build: From c2ab1ffa6a015fe8849ad925d483f9a7a8fe627a Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 4 Dec 2024 15:57:08 -0600 Subject: [PATCH 57/59] Add `shell: bash` config to `Build Python docs` step of docs workflow --- .github/workflows/docs.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 1bdd911..7003b59 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -50,6 +50,7 @@ jobs: - name: Build Python docs run: uv run sphinx-build python/docs/source docs/python + shell: bash - name: Configure pages uses: actions/configure-pages@v5 From dd2d922ed4350fb7a98017215b836878d2b7c63d Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 4 Dec 2024 16:02:32 -0600 Subject: [PATCH 58/59] Add tmate to docs workflow for debugging --- .github/workflows/docs.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 7003b59..8702c07 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -48,6 +48,9 @@ jobs: run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) shell: Rscript {0} + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + - name: Build Python docs run: uv run sphinx-build python/docs/source docs/python shell: bash From c771664bd9d66ce44c336371ecf04794f8cec746 Mon Sep 17 00:00:00 2001 From: Jean Cochrane Date: Wed, 4 Dec 2024 16:10:10 -0600 Subject: [PATCH 59/59] Run sphinx-build from the correct working directory in docs workflow --- .github/workflows/docs.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 8702c07..c676a47 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -48,11 +48,9 @@ jobs: run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) shell: Rscript {0} - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - - name: Build Python docs - run: uv run sphinx-build python/docs/source docs/python + working-directory: python + run: uv run sphinx-build docs/source ../docs/python shell: bash - name: Configure pages