diff --git a/.gitignore b/.gitignore index 2200d827..f33ad7dd 100644 --- a/.gitignore +++ b/.gitignore @@ -119,10 +119,8 @@ celerybeat.pid # Environments .env .venv -env/ venv/ ENV/ -env.bak/ venv.bak/ # Spyder project settings diff --git a/CHANGELOG.md b/CHANGELOG.md index 088e8072..c1fa22f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,12 +12,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New class `logs.LoggingSetupHelper`. - New functions `logs.get_logger()` and `convert_log_level()`. - New ***naive*** functions `strings.camel_to_snake()` and `snake_to_camel()`. +- New class `types.LiteralHelper[T]`. +- New wrapper functions `types.verify_literal()` and `verify_enum()`. +- New module `rics.env`: + * Added `read`-functions for primitive types; `read_bool()`, `read_int`, `read_enum()`. + * Added `types.LiteralHelper[T].read_env()`. +- New function `strings.str_as_bool()`. ### Changed - Update `basic_config.basic_config()`: Allow and handle `level=None` to avoid logging from root. - The `misc.get_by_full_name()` function now supports reading member attributes. Uses [entrypoint](https://packaging.python.org/en/latest/specifications/entry-points/) syntax, e.g. `pandas:DataFrame.sum`. +- Moved some functions to new `rics.env` module: + * Moves implementation `rics.envinterp` -> `rics.env.interpolation`. + * Moved implementation of `misc.interpolate_environment_variables()` -> `env.interpolation.replace_in_string()`. + + Aliases above will be deprecated in `0.6.0` and removed in `0.7.0`. ### Fixed - Calling `MultiCaseTimer.run(number=)` no longer crashes. diff --git a/poetry.lock b/poetry.lock index 94f96372..e605023b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -384,104 +384,104 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "3.4.1" +version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["devops", "docs", "notebooks"] files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, + {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, + {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, ] [[package]] @@ -733,47 +733,49 @@ pytz = ">2021.1" [[package]] name = "cryptography" -version = "44.0.2" +version = "44.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.7" groups = ["devops"] files = [ - {file = "cryptography-44.0.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308"}, - {file = "cryptography-44.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688"}, - {file = "cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7"}, - {file = "cryptography-44.0.2-cp37-abi3-win32.whl", hash = "sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79"}, - {file = "cryptography-44.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa"}, - {file = "cryptography-44.0.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23"}, - {file = "cryptography-44.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922"}, - {file = "cryptography-44.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4"}, - {file = "cryptography-44.0.2-cp39-abi3-win32.whl", hash = "sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5"}, - {file = "cryptography-44.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:af4ff3e388f2fa7bff9f7f2b31b87d5651c45731d3e8cfa0944be43dff5cfbdb"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:0529b1d5a0105dd3731fa65680b45ce49da4d8115ea76e9da77a875396727b41"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7ca25849404be2f8e4b3c59483d9d3c51298a22c1c61a0e84415104dacaf5562"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:268e4e9b177c76d569e8a145a6939eca9a5fec658c932348598818acf31ae9a5"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:9eb9d22b0a5d8fd9925a7764a054dca914000607dff201a24c791ff5c799e1fa"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2bf7bf75f7df9715f810d1b038870309342bff3069c5bd8c6b96128cb158668d"}, - {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:909c97ab43a9c0c0b0ada7a1281430e4e5ec0458e6d9244c0e821bbf152f061d"}, - {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:96e7a5e9d6e71f9f4fca8eebfd603f8e86c5225bb18eb621b2c1e50b290a9471"}, - {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d1b3031093a366ac767b3feb8bcddb596671b3aaff82d4050f984da0c248b615"}, - {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390"}, - {file = "cryptography-44.0.2.tar.gz", hash = "sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0"}, + {file = "cryptography-44.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:962bc30480a08d133e631e8dfd4783ab71cc9e33d5d7c1e192f0b7c06397bb88"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffc61e8f3bf5b60346d89cd3d37231019c17a081208dfbbd6e1605ba03fa137"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58968d331425a6f9eedcee087f77fd3c927c88f55368f43ff7e0a19891f2642c"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e28d62e59a4dbd1d22e747f57d4f00c459af22181f0b2f787ea83f5a876d7c76"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af653022a0c25ef2e3ffb2c673a50e5a0d02fecc41608f4954176f1933b12359"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:157f1f3b8d941c2bd8f3ffee0af9b049c9665c39d3da9db2dc338feca5e98a43"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:c6cd67722619e4d55fdb42ead64ed8843d64638e9c07f4011163e46bc512cf01"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b424563394c369a804ecbee9b06dfb34997f19d00b3518e39f83a5642618397d"}, + {file = "cryptography-44.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c91fc8e8fd78af553f98bc7f2a1d8db977334e4eea302a4bfd75b9461c2d8904"}, + {file = "cryptography-44.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:25cd194c39fa5a0aa4169125ee27d1172097857b27109a45fadc59653ec06f44"}, + {file = "cryptography-44.0.3-cp37-abi3-win32.whl", hash = "sha256:3be3f649d91cb182c3a6bd336de8b61a0a71965bd13d1a04a0e15b39c3d5809d"}, + {file = "cryptography-44.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:3883076d5c4cc56dbef0b898a74eb6992fdac29a7b9013870b34efe4ddb39a0d"}, + {file = "cryptography-44.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:5639c2b16764c6f76eedf722dbad9a0914960d3489c0cc38694ddf9464f1bb2f"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ffef566ac88f75967d7abd852ed5f182da252d23fac11b4766da3957766759"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:192ed30fac1728f7587c6f4613c29c584abdc565d7417c13904708db10206645"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7d5fe7195c27c32a64955740b949070f21cba664604291c298518d2e255931d2"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3f07943aa4d7dad689e3bb1638ddc4944cc5e0921e3c227486daae0e31a05e54"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb90f60e03d563ca2445099edf605c16ed1d5b15182d21831f58460c48bffb93"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ab0b005721cc0039e885ac3503825661bd9810b15d4f374e473f8c89b7d5460c"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3bb0847e6363c037df8f6ede57d88eaf3410ca2267fb12275370a76f85786a6f"}, + {file = "cryptography-44.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b0cc66c74c797e1db750aaa842ad5b8b78e14805a9b5d1348dc603612d3e3ff5"}, + {file = "cryptography-44.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6866df152b581f9429020320e5eb9794c8780e90f7ccb021940d7f50ee00ae0b"}, + {file = "cryptography-44.0.3-cp39-abi3-win32.whl", hash = "sha256:c138abae3a12a94c75c10499f1cbae81294a6f983b3af066390adee73f433028"}, + {file = "cryptography-44.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:5d186f32e52e66994dce4f766884bcb9c68b8da62d61d9d215bfe5fb56d21334"}, + {file = "cryptography-44.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:cad399780053fb383dc067475135e41c9fe7d901a97dd5d9c5dfb5611afc0d7d"}, + {file = "cryptography-44.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:21a83f6f35b9cc656d71b5de8d519f566df01e660ac2578805ab245ffd8523f8"}, + {file = "cryptography-44.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fc3c9babc1e1faefd62704bb46a69f359a9819eb0292e40df3fb6e3574715cd4"}, + {file = "cryptography-44.0.3-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:e909df4053064a97f1e6565153ff8bb389af12c5c8d29c343308760890560aff"}, + {file = "cryptography-44.0.3-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:dad80b45c22e05b259e33ddd458e9e2ba099c86ccf4e88db7bbab4b747b18d06"}, + {file = "cryptography-44.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:479d92908277bed6e1a1c69b277734a7771c2b78633c224445b5c60a9f4bc1d9"}, + {file = "cryptography-44.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:896530bc9107b226f265effa7ef3f21270f18a2026bc09fed1ebd7b66ddf6375"}, + {file = "cryptography-44.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9b4d4a5dbee05a2c390bf212e78b99434efec37b17a4bff42f50285c5c8c9647"}, + {file = "cryptography-44.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02f55fb4f8b79c1221b0961488eaae21015b69b210e18c386b69de182ebb1259"}, + {file = "cryptography-44.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:dd3db61b8fe5be220eee484a17233287d0be6932d056cf5738225b9c05ef4fff"}, + {file = "cryptography-44.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:978631ec51a6bbc0b7e58f23b68a8ce9e5f09721940933e9c217068388789fe5"}, + {file = "cryptography-44.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:5d20cc348cca3a8aa7312f42ab953a56e15323800ca3ab0706b8cd452a3a056c"}, + {file = "cryptography-44.0.3.tar.gz", hash = "sha256:fe19d8bc5536a91a24a8133328880a41831b6c5df54599a8417b62fe015d3053"}, ] [package.dependencies] @@ -786,7 +788,7 @@ nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_version >= \"3.8\""] pep8test = ["check-sdist ; python_version >= \"3.8\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==44.0.2)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==44.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] @@ -1208,15 +1210,15 @@ files = [ [[package]] name = "importlib-metadata" -version = "8.6.1" +version = "8.7.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["test"] markers = "python_version == \"3.11\"" files = [ - {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, - {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, + {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, + {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, ] [package.dependencies] @@ -1404,14 +1406,14 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "joblib" -version = "1.4.2" +version = "1.5.0" description = "Lightweight pipelining with Python functions" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["devops", "test"] files = [ - {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, - {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, + {file = "joblib-1.5.0-py3-none-any.whl", hash = "sha256:206144b320246485b712fc8cc51f017de58225fa8b414a1fe1764a7231aca491"}, + {file = "joblib-1.5.0.tar.gz", hash = "sha256:d8757f955389a3dd7a23152e43bc297c2e0c2d3060056dad0feefc88a06939b5"}, ] [[package]] @@ -2687,19 +2689,19 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "polars" -version = "1.27.1" +version = "1.29.0" description = "Blazingly fast DataFrame library" optional = false python-versions = ">=3.9" groups = ["manual-extras"] files = [ - {file = "polars-1.27.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:ba7ad4f8046d00dd97c1369e46a4b7e00ffcff5d38c0f847ee4b9b1bb182fb18"}, - {file = "polars-1.27.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:339e3948748ad6fa7a42e613c3fb165b497ed797e93fce1aa2cddf00fbc16cac"}, - {file = "polars-1.27.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f801e0d9da198eb97cfb4e8af4242b8396878ff67b655c71570b7e333102b72b"}, - {file = "polars-1.27.1-cp39-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:4d18a29c65222451818b63cd397b2e95c20412ea0065d735a20a4a79a7b26e8a"}, - {file = "polars-1.27.1-cp39-abi3-win_amd64.whl", hash = "sha256:a4f832cf478b282d97f8bf86eeae2df66fa1384de1c49bc61f7224a10cc6a5df"}, - {file = "polars-1.27.1-cp39-abi3-win_arm64.whl", hash = "sha256:4f238ee2e3c5660345cb62c0f731bbd6768362db96c058098359ecffa42c3c6c"}, - {file = "polars-1.27.1.tar.gz", hash = "sha256:94fcb0216b56cd0594aa777db1760a41ad0dfffed90d2ca446cf9294d2e97f02"}, + {file = "polars-1.29.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:d053ee3217df31468caf2f5ddb9fd0f3a94fd42afdf7d9abe23d9d424adca02b"}, + {file = "polars-1.29.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:14131078e365eae5ccda3e67383cd43c0c0598d7f760bdf1cb4082566c5494ce"}, + {file = "polars-1.29.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54f6902da333f99208b8d27765d580ba0299b412787c0564275912122c228e40"}, + {file = "polars-1.29.0-cp39-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:7a0ac6a11088279af4d715f4b58068835f551fa5368504a53401743006115e78"}, + {file = "polars-1.29.0-cp39-abi3-win_amd64.whl", hash = "sha256:f5aac4656e58b1e12f9481950981ef68b5b0e53dd4903bd72472efd2d09a74c8"}, + {file = "polars-1.29.0-cp39-abi3-win_arm64.whl", hash = "sha256:0c105b07b980b77fe88c3200b015bf4695e53185385f0f244c13e2d1027c7bbf"}, + {file = "polars-1.29.0.tar.gz", hash = "sha256:d2acb71fce1ff0ea76db5f648abd91a7a6c460fafabce9a2e8175184efa00d02"}, ] [package.dependencies] @@ -2824,54 +2826,67 @@ tests = ["pytest"] [[package]] name = "pyarrow" -version = "19.0.1" +version = "20.0.0" description = "Python library for Apache Arrow" optional = false python-versions = ">=3.9" groups = ["manual-extras", "test"] files = [ - {file = "pyarrow-19.0.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:fc28912a2dc924dddc2087679cc8b7263accc71b9ff025a1362b004711661a69"}, - {file = "pyarrow-19.0.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:fca15aabbe9b8355800d923cc2e82c8ef514af321e18b437c3d782aa884eaeec"}, - {file = "pyarrow-19.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad76aef7f5f7e4a757fddcdcf010a8290958f09e3470ea458c80d26f4316ae89"}, - {file = "pyarrow-19.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d03c9d6f2a3dffbd62671ca070f13fc527bb1867b4ec2b98c7eeed381d4f389a"}, - {file = "pyarrow-19.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:65cf9feebab489b19cdfcfe4aa82f62147218558d8d3f0fc1e9dea0ab8e7905a"}, - {file = "pyarrow-19.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:41f9706fbe505e0abc10e84bf3a906a1338905cbbcf1177b71486b03e6ea6608"}, - {file = "pyarrow-19.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6cb2335a411b713fdf1e82a752162f72d4a7b5dbc588e32aa18383318b05866"}, - {file = "pyarrow-19.0.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:cc55d71898ea30dc95900297d191377caba257612f384207fe9f8293b5850f90"}, - {file = "pyarrow-19.0.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:7a544ec12de66769612b2d6988c36adc96fb9767ecc8ee0a4d270b10b1c51e00"}, - {file = "pyarrow-19.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0148bb4fc158bfbc3d6dfe5001d93ebeed253793fff4435167f6ce1dc4bddeae"}, - {file = "pyarrow-19.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f24faab6ed18f216a37870d8c5623f9c044566d75ec586ef884e13a02a9d62c5"}, - {file = "pyarrow-19.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4982f8e2b7afd6dae8608d70ba5bd91699077323f812a0448d8b7abdff6cb5d3"}, - {file = "pyarrow-19.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:49a3aecb62c1be1d822f8bf629226d4a96418228a42f5b40835c1f10d42e4db6"}, - {file = "pyarrow-19.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:008a4009efdb4ea3d2e18f05cd31f9d43c388aad29c636112c2966605ba33466"}, - {file = "pyarrow-19.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b"}, - {file = "pyarrow-19.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294"}, - {file = "pyarrow-19.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14"}, - {file = "pyarrow-19.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34"}, - {file = "pyarrow-19.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6"}, - {file = "pyarrow-19.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832"}, - {file = "pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960"}, - {file = "pyarrow-19.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e45274b20e524ae5c39d7fc1ca2aa923aab494776d2d4b316b49ec7572ca324c"}, - {file = "pyarrow-19.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d9dedeaf19097a143ed6da37f04f4051aba353c95ef507764d344229b2b740ae"}, - {file = "pyarrow-19.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ebfb5171bb5f4a52319344ebbbecc731af3f021e49318c74f33d520d31ae0c4"}, - {file = "pyarrow-19.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a21d39fbdb948857f67eacb5bbaaf36802de044ec36fbef7a1c8f0dd3a4ab2"}, - {file = "pyarrow-19.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:99bc1bec6d234359743b01e70d4310d0ab240c3d6b0da7e2a93663b0158616f6"}, - {file = "pyarrow-19.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1b93ef2c93e77c442c979b0d596af45e4665d8b96da598db145b0fec014b9136"}, - {file = "pyarrow-19.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:d9d46e06846a41ba906ab25302cf0fd522f81aa2a85a71021826f34639ad31ef"}, - {file = "pyarrow-19.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c0fe3dbbf054a00d1f162fda94ce236a899ca01123a798c561ba307ca38af5f0"}, - {file = "pyarrow-19.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:96606c3ba57944d128e8a8399da4812f56c7f61de8c647e3470b417f795d0ef9"}, - {file = "pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f04d49a6b64cf24719c080b3c2029a3a5b16417fd5fd7c4041f94233af732f3"}, - {file = "pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9137cf7e1640dce4c190551ee69d478f7121b5c6f323553b319cac936395f6"}, - {file = "pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:7c1bca1897c28013db5e4c83944a2ab53231f541b9e0c3f4791206d0c0de389a"}, - {file = "pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:58d9397b2e273ef76264b45531e9d552d8ec8a6688b7390b5be44c02a37aade8"}, - {file = "pyarrow-19.0.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:b9766a47a9cb56fefe95cb27f535038b5a195707a08bf61b180e642324963b46"}, - {file = "pyarrow-19.0.1-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:6c5941c1aac89a6c2f2b16cd64fe76bcdb94b2b1e99ca6459de4e6f07638d755"}, - {file = "pyarrow-19.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd44d66093a239358d07c42a91eebf5015aa54fccba959db899f932218ac9cc8"}, - {file = "pyarrow-19.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:335d170e050bcc7da867a1ed8ffb8b44c57aaa6e0843b156a501298657b1e972"}, - {file = "pyarrow-19.0.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:1c7556165bd38cf0cd992df2636f8bcdd2d4b26916c6b7e646101aff3c16f76f"}, - {file = "pyarrow-19.0.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:699799f9c80bebcf1da0983ba86d7f289c5a2a5c04b945e2f2bcf7e874a91911"}, - {file = "pyarrow-19.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:8464c9fbe6d94a7fe1599e7e8965f350fd233532868232ab2596a71586c5a429"}, - {file = "pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e"}, + {file = "pyarrow-20.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:c7dd06fd7d7b410ca5dc839cc9d485d2bc4ae5240851bcd45d85105cc90a47d7"}, + {file = "pyarrow-20.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:d5382de8dc34c943249b01c19110783d0d64b207167c728461add1ecc2db88e4"}, + {file = "pyarrow-20.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6415a0d0174487456ddc9beaead703d0ded5966129fa4fd3114d76b5d1c5ceae"}, + {file = "pyarrow-20.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15aa1b3b2587e74328a730457068dc6c89e6dcbf438d4369f572af9d320a25ee"}, + {file = "pyarrow-20.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5605919fbe67a7948c1f03b9f3727d82846c053cd2ce9303ace791855923fd20"}, + {file = "pyarrow-20.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a5704f29a74b81673d266e5ec1fe376f060627c2e42c5c7651288ed4b0db29e9"}, + {file = "pyarrow-20.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:00138f79ee1b5aca81e2bdedb91e3739b987245e11fa3c826f9e57c5d102fb75"}, + {file = "pyarrow-20.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f2d67ac28f57a362f1a2c1e6fa98bfe2f03230f7e15927aecd067433b1e70ce8"}, + {file = "pyarrow-20.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:4a8b029a07956b8d7bd742ffca25374dd3f634b35e46cc7a7c3fa4c75b297191"}, + {file = "pyarrow-20.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:24ca380585444cb2a31324c546a9a56abbe87e26069189e14bdba19c86c049f0"}, + {file = "pyarrow-20.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:95b330059ddfdc591a3225f2d272123be26c8fa76e8c9ee1a77aad507361cfdb"}, + {file = "pyarrow-20.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f0fb1041267e9968c6d0d2ce3ff92e3928b243e2b6d11eeb84d9ac547308232"}, + {file = "pyarrow-20.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8ff87cc837601532cc8242d2f7e09b4e02404de1b797aee747dd4ba4bd6313f"}, + {file = "pyarrow-20.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:7a3a5dcf54286e6141d5114522cf31dd67a9e7c9133d150799f30ee302a7a1ab"}, + {file = "pyarrow-20.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a6ad3e7758ecf559900261a4df985662df54fb7fdb55e8e3b3aa99b23d526b62"}, + {file = "pyarrow-20.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6bb830757103a6cb300a04610e08d9636f0cd223d32f388418ea893a3e655f1c"}, + {file = "pyarrow-20.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96e37f0766ecb4514a899d9a3554fadda770fb57ddf42b63d80f14bc20aa7db3"}, + {file = "pyarrow-20.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3346babb516f4b6fd790da99b98bed9708e3f02e734c84971faccb20736848dc"}, + {file = "pyarrow-20.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:75a51a5b0eef32727a247707d4755322cb970be7e935172b6a3a9f9ae98404ba"}, + {file = "pyarrow-20.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:211d5e84cecc640c7a3ab900f930aaff5cd2702177e0d562d426fb7c4f737781"}, + {file = "pyarrow-20.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ba3cf4182828be7a896cbd232aa8dd6a31bd1f9e32776cc3796c012855e1199"}, + {file = "pyarrow-20.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c3a01f313ffe27ac4126f4c2e5ea0f36a5fc6ab51f8726cf41fee4b256680bd"}, + {file = "pyarrow-20.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a2791f69ad72addd33510fec7bb14ee06c2a448e06b649e264c094c5b5f7ce28"}, + {file = "pyarrow-20.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4250e28a22302ce8692d3a0e8ec9d9dde54ec00d237cff4dfa9c1fbf79e472a8"}, + {file = "pyarrow-20.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:89e030dc58fc760e4010148e6ff164d2f44441490280ef1e97a542375e41058e"}, + {file = "pyarrow-20.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6102b4864d77102dbbb72965618e204e550135a940c2534711d5ffa787df2a5a"}, + {file = "pyarrow-20.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:96d6a0a37d9c98be08f5ed6a10831d88d52cac7b13f5287f1e0f625a0de8062b"}, + {file = "pyarrow-20.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a15532e77b94c61efadde86d10957950392999503b3616b2ffcef7621a002893"}, + {file = "pyarrow-20.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:dd43f58037443af715f34f1322c782ec463a3c8a94a85fdb2d987ceb5658e061"}, + {file = "pyarrow-20.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa0d288143a8585806e3cc7c39566407aab646fb9ece164609dac1cfff45f6ae"}, + {file = "pyarrow-20.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6953f0114f8d6f3d905d98e987d0924dabce59c3cda380bdfaa25a6201563b4"}, + {file = "pyarrow-20.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:991f85b48a8a5e839b2128590ce07611fae48a904cae6cab1f089c5955b57eb5"}, + {file = "pyarrow-20.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:97c8dc984ed09cb07d618d57d8d4b67a5100a30c3818c2fb0b04599f0da2de7b"}, + {file = "pyarrow-20.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9b71daf534f4745818f96c214dbc1e6124d7daf059167330b610fc69b6f3d3e3"}, + {file = "pyarrow-20.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8b88758f9303fa5a83d6c90e176714b2fd3852e776fc2d7e42a22dd6c2fb368"}, + {file = "pyarrow-20.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:30b3051b7975801c1e1d387e17c588d8ab05ced9b1e14eec57915f79869b5031"}, + {file = "pyarrow-20.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:ca151afa4f9b7bc45bcc791eb9a89e90a9eb2772767d0b1e5389609c7d03db63"}, + {file = "pyarrow-20.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:4680f01ecd86e0dd63e39eb5cd59ef9ff24a9d166db328679e36c108dc993d4c"}, + {file = "pyarrow-20.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f4c8534e2ff059765647aa69b75d6543f9fef59e2cd4c6d18015192565d2b70"}, + {file = "pyarrow-20.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e1f8a47f4b4ae4c69c4d702cfbdfe4d41e18e5c7ef6f1bb1c50918c1e81c57b"}, + {file = "pyarrow-20.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:a1f60dc14658efaa927f8214734f6a01a806d7690be4b3232ba526836d216122"}, + {file = "pyarrow-20.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:204a846dca751428991346976b914d6d2a82ae5b8316a6ed99789ebf976551e6"}, + {file = "pyarrow-20.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f3b117b922af5e4c6b9a9115825726cac7d8b1421c37c2b5e24fbacc8930612c"}, + {file = "pyarrow-20.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e724a3fd23ae5b9c010e7be857f4405ed5e679db5c93e66204db1a69f733936a"}, + {file = "pyarrow-20.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:82f1ee5133bd8f49d31be1299dc07f585136679666b502540db854968576faf9"}, + {file = "pyarrow-20.0.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:1bcbe471ef3349be7714261dea28fe280db574f9d0f77eeccc195a2d161fd861"}, + {file = "pyarrow-20.0.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:a18a14baef7d7ae49247e75641fd8bcbb39f44ed49a9fc4ec2f65d5031aa3b96"}, + {file = "pyarrow-20.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb497649e505dc36542d0e68eca1a3c94ecbe9799cb67b578b55f2441a247fbc"}, + {file = "pyarrow-20.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11529a2283cb1f6271d7c23e4a8f9f8b7fd173f7360776b668e509d712a02eec"}, + {file = "pyarrow-20.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fc1499ed3b4b57ee4e090e1cea6eb3584793fe3d1b4297bbf53f09b434991a5"}, + {file = "pyarrow-20.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:db53390eaf8a4dab4dbd6d93c85c5cf002db24902dbff0ca7d988beb5c9dd15b"}, + {file = "pyarrow-20.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:851c6a8260ad387caf82d2bbf54759130534723e37083111d4ed481cb253cc0d"}, + {file = "pyarrow-20.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e22f80b97a271f0a7d9cd07394a7d348f80d3ac63ed7cc38b6d1b696ab3b2619"}, + {file = "pyarrow-20.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:9965a050048ab02409fb7cbbefeedba04d3d67f2cc899eff505cc084345959ca"}, + {file = "pyarrow-20.0.0.tar.gz", hash = "sha256:febc4a913592573c8d5805091a6c2b5064c8bd6e002131f01061797d91c783c1"}, ] [package.extras] @@ -3762,30 +3777,30 @@ files = [ [[package]] name = "ruff" -version = "0.11.7" +version = "0.11.8" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["devops"] files = [ - {file = "ruff-0.11.7-py3-none-linux_armv6l.whl", hash = "sha256:d29e909d9a8d02f928d72ab7837b5cbc450a5bdf578ab9ebee3263d0a525091c"}, - {file = "ruff-0.11.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dd1fb86b168ae349fb01dd497d83537b2c5541fe0626e70c786427dd8363aaee"}, - {file = "ruff-0.11.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d3d7d2e140a6fbbc09033bce65bd7ea29d6a0adeb90b8430262fbacd58c38ada"}, - {file = "ruff-0.11.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4809df77de390a1c2077d9b7945d82f44b95d19ceccf0c287c56e4dc9b91ca64"}, - {file = "ruff-0.11.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3a0c2e169e6b545f8e2dba185eabbd9db4f08880032e75aa0e285a6d3f48201"}, - {file = "ruff-0.11.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49b888200a320dd96a68e86736cf531d6afba03e4f6cf098401406a257fcf3d6"}, - {file = "ruff-0.11.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2b19cdb9cf7dae00d5ee2e7c013540cdc3b31c4f281f1dacb5a799d610e90db4"}, - {file = "ruff-0.11.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64e0ee994c9e326b43539d133a36a455dbaab477bc84fe7bfbd528abe2f05c1e"}, - {file = "ruff-0.11.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bad82052311479a5865f52c76ecee5d468a58ba44fb23ee15079f17dd4c8fd63"}, - {file = "ruff-0.11.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7940665e74e7b65d427b82bffc1e46710ec7f30d58b4b2d5016e3f0321436502"}, - {file = "ruff-0.11.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:169027e31c52c0e36c44ae9a9c7db35e505fee0b39f8d9fca7274a6305295a92"}, - {file = "ruff-0.11.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:305b93f9798aee582e91e34437810439acb28b5fc1fee6b8205c78c806845a94"}, - {file = "ruff-0.11.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a681db041ef55550c371f9cd52a3cf17a0da4c75d6bd691092dfc38170ebc4b6"}, - {file = "ruff-0.11.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:07f1496ad00a4a139f4de220b0c97da6d4c85e0e4aa9b2624167b7d4d44fd6b6"}, - {file = "ruff-0.11.7-py3-none-win32.whl", hash = "sha256:f25dfb853ad217e6e5f1924ae8a5b3f6709051a13e9dad18690de6c8ff299e26"}, - {file = "ruff-0.11.7-py3-none-win_amd64.whl", hash = "sha256:0a931d85959ceb77e92aea4bbedfded0a31534ce191252721128f77e5ae1f98a"}, - {file = "ruff-0.11.7-py3-none-win_arm64.whl", hash = "sha256:778c1e5d6f9e91034142dfd06110534ca13220bfaad5c3735f6cb844654f6177"}, - {file = "ruff-0.11.7.tar.gz", hash = "sha256:655089ad3224070736dc32844fde783454f8558e71f501cb207485fe4eee23d4"}, + {file = "ruff-0.11.8-py3-none-linux_armv6l.whl", hash = "sha256:896a37516c594805e34020c4a7546c8f8a234b679a7716a3f08197f38913e1a3"}, + {file = "ruff-0.11.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab86d22d3d721a40dd3ecbb5e86ab03b2e053bc93c700dc68d1c3346b36ce835"}, + {file = "ruff-0.11.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:258f3585057508d317610e8a412788cf726efeefa2fec4dba4001d9e6f90d46c"}, + {file = "ruff-0.11.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:727d01702f7c30baed3fc3a34901a640001a2828c793525043c29f7614994a8c"}, + {file = "ruff-0.11.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3dca977cc4fc8f66e89900fa415ffe4dbc2e969da9d7a54bfca81a128c5ac219"}, + {file = "ruff-0.11.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c657fa987d60b104d2be8b052d66da0a2a88f9bd1d66b2254333e84ea2720c7f"}, + {file = "ruff-0.11.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f2e74b021d0de5eceb8bd32919f6ff8a9b40ee62ed97becd44993ae5b9949474"}, + {file = "ruff-0.11.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9b5ef39820abc0f2c62111f7045009e46b275f5b99d5e59dda113c39b7f4f38"}, + {file = "ruff-0.11.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1dba3135ca503727aa4648152c0fa67c3b1385d3dc81c75cd8a229c4b2a1458"}, + {file = "ruff-0.11.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f024d32e62faad0f76b2d6afd141b8c171515e4fb91ce9fd6464335c81244e5"}, + {file = "ruff-0.11.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d365618d3ad747432e1ae50d61775b78c055fee5936d77fb4d92c6f559741948"}, + {file = "ruff-0.11.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4d9aaa91035bdf612c8ee7266153bcf16005c7c7e2f5878406911c92a31633cb"}, + {file = "ruff-0.11.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0eba551324733efc76116d9f3a0d52946bc2751f0cd30661564117d6fd60897c"}, + {file = "ruff-0.11.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:161eb4cff5cfefdb6c9b8b3671d09f7def2f960cee33481dd898caf2bcd02304"}, + {file = "ruff-0.11.8-py3-none-win32.whl", hash = "sha256:5b18caa297a786465cc511d7f8be19226acf9c0a1127e06e736cd4e1878c3ea2"}, + {file = "ruff-0.11.8-py3-none-win_amd64.whl", hash = "sha256:6e70d11043bef637c5617297bdedec9632af15d53ac1e1ba29c448da9341b0c4"}, + {file = "ruff-0.11.8-py3-none-win_arm64.whl", hash = "sha256:304432e4c4a792e3da85b7699feb3426a0908ab98bf29df22a31b0cdd098fac2"}, + {file = "ruff-0.11.8.tar.gz", hash = "sha256:6d742d10626f9004b781f4558154bb226620a7242080e11caeffab1a40e99df8"}, ] [[package]] @@ -4006,14 +4021,14 @@ win32 = ["pywin32 ; sys_platform == \"win32\""] [[package]] name = "setuptools" -version = "79.0.1" +version = "80.3.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" groups = ["devops", "notebooks"] files = [ - {file = "setuptools-79.0.1-py3-none-any.whl", hash = "sha256:e147c0549f27767ba362f9da434eab9c5dc0045d5304feb602a0af001089fc51"}, - {file = "setuptools-79.0.1.tar.gz", hash = "sha256:128ce7b8f33c3079fd1b067ecbb4051a66e8526e7b65f6cec075dfc650ddfa88"}, + {file = "setuptools-80.3.0-py3-none-any.whl", hash = "sha256:a65cffc4fb86167e3020b3ef58e08226baad8b29a3b34ce2c9d07e901bac481d"}, + {file = "setuptools-80.3.0.tar.gz", hash = "sha256:ec8308eb180b2312062b1c5523204acf872cd8b0a9e6c2ae76431b22bc4065d7"}, ] [package.extras] @@ -4394,14 +4409,14 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "typer" -version = "0.15.2" +version = "0.15.3" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" groups = ["devops"] files = [ - {file = "typer-0.15.2-py3-none-any.whl", hash = "sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc"}, - {file = "typer-0.15.2.tar.gz", hash = "sha256:ab2fab47533a813c49fe1f16b1a370fd5819099c00b119e0633df65f22144ba5"}, + {file = "typer-0.15.3-py3-none-any.whl", hash = "sha256:c86a65ad77ca531f03de08d1b9cb67cd09ad02ddddf4b34745b5008f43b239bd"}, + {file = "typer-0.15.3.tar.gz", hash = "sha256:818873625d0569653438316567861899f7e9972f2e6e0c16dab608345ced713c"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index a29c668a..ffcab788 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -161,13 +161,6 @@ ignore = [ "D205", # Clashes with sphinx_gallery (generated examples) "B007", ] -"**/time_split/*" = [ - "PLR0913", # Too many arguments in function definition -] -"**/time_split/integration/sklearn/*" = [ - "N803", # Argument name `X` should be lowercase - "ARG002", # Unused method argument: `groups` -] [tool.ruff.lint.pydocstyle] convention = "google" diff --git a/src/rics/env/__init__.py b/src/rics/env/__init__.py new file mode 100644 index 00000000..902ad4ea --- /dev/null +++ b/src/rics/env/__init__.py @@ -0,0 +1 @@ +"""Utilities for working with environment variables.""" diff --git a/src/rics/env/interpolation/__init__.py b/src/rics/env/interpolation/__init__.py new file mode 100644 index 00000000..2d63a895 --- /dev/null +++ b/src/rics/env/interpolation/__init__.py @@ -0,0 +1,65 @@ +"""Environment variable interpolation in files and strings. + +This module provides utilities to read text and replace environment variable references with the actual environment +variable value using a familiar syntax. Optional default values are also possible, as well as recursively nested +variables (must be explicitly enabled). + +Syntax: + Similar to Bash variable interpolation; ``${}``, where ```` is the name of the environment variable you + wish to extract. This particular form will raise an exception if ```` is not set. + +Default values: + Default values are specified by stating the default value after a colon; ``${:default-value}``. The default + value may be blank, in which case an empty string is used as the default. + +There are three main ways of using this module: + + * The :meth:`.Variable.parse_string`-function, combined with :meth:`.Variable.get_value`. This gives you the most + amount of control. + * The :func:`replace_in_string`-function, for basic interpolation without recursion in an entire string. + * The :func:`rics.misc.interpolate_environment_variables`-function, which adds some additional logic on top of what + the :class:`Variable`-methods provide. + +Examples: + We'll set ``ENV_VAR0=VALUE0`` and ``ENV_VAR1=VALUE1``. Vars 2 and 4 are not set. + + >>> from os import environ + >>> environ["ENV_VAR0"] = "VALUE0" + >>> environ["ENV_VAR1"] = "VALUE1" + + Basic interpolation. + + >>> Variable.parse_first("${ENV_VAR0}").get_value() + 'VALUE0' + + Setting a default works as expected. The default may also be empty. + + >>> Variable.parse_first("${ENV_VAR2:default}").get_value() + 'default' + >>> Variable.parse_first("${ENV_VAR2:}").get_value() + '' + + The variable must exist if no default is given. + + >>> Variable.parse_first("${DOESNT_EXIST}").get_value() # doctest: +SKIP + UnsetVariableError: Required Environment Variable 'DOESNT_EXIST': Not set. + + Furthermore, variables may be nested, but this requires setting the flag to enable recursive parsing. + + >>> Variable.parse_first("${ ENV_VAR2 :${ ENV_VAR0 }}").get_value( + ... resolve_nested_defaults=True + ... ) + 'VALUE0' + + Whitespaces around the names is fine. Finally, nesting may be arbitrarily deep. + + >>> Variable.parse_first("${ENV_VAR3:${ENV_VAR4:${ENV_VAR0}}}").get_value(True) + 'VALUE0' + + TODO infinite recursion ovan? +""" + +from ._file_utils import replace_in_string +from ._variable import UnsetVariableError, Variable + +__all__ = ["UnsetVariableError", "Variable", "replace_in_string"] diff --git a/src/rics/env/interpolation/_file_utils.py b/src/rics/env/interpolation/_file_utils.py new file mode 100644 index 00000000..7ff586b4 --- /dev/null +++ b/src/rics/env/interpolation/_file_utils.py @@ -0,0 +1,40 @@ +from ._variable import UnsetVariableError, Variable + + +def replace_in_string( + s: str, + *, + allow_nested: bool = True, + allow_blank: bool = False, +) -> str: + """Interpolate environment variables in a string `s`. + + This function replaces references to environment variables with the actual value of the variable, or a default if + specified. The syntax is similar to Bash string interpolation; use ``${}`` for mandatory variables, and + ``${:default}`` for optional variables. + + Args: + s: A string in which to interpolate. + allow_blank: If ``True``, allow variables to be set but empty. + allow_nested: If ``True`` allow using another environment variable as the default value. This option will not + verify whether the actual values are interpolation-strings. + + Returns: + A copy of `s`, after environment variable interpolation. + + Raises: + ValueError: If nested variables are discovered (only when ``allow_nested=False``). + UnsetVariableError: If any required environment variables are unset or blank (only when ``allow_blank=False``). + """ + for var in Variable.parse_string(s): + if not allow_nested and (var.default and Variable.parse_string(var.default)): + raise ValueError(f"Nested variables forbidden since {allow_nested=}.") + + value = var.get_value(resolve_nested_defaults=allow_nested).strip() + + if not (allow_blank or value): + msg = f"Empty values forbidden since {allow_blank=}." + raise UnsetVariableError(var.name, msg) + + s = s.replace(var.full_match, value) + return s diff --git a/src/rics/envinterp/_variable.py b/src/rics/env/interpolation/_variable.py similarity index 100% rename from src/rics/envinterp/_variable.py rename to src/rics/env/interpolation/_variable.py diff --git a/src/rics/env/read/__init__.py b/src/rics/env/read/__init__.py new file mode 100644 index 00000000..573d1a61 --- /dev/null +++ b/src/rics/env/read/__init__.py @@ -0,0 +1,19 @@ +"""Read environment variables as specific types. + +To read ``typing.Literal`` values, use :meth:`rics.types.LiteralHelper.read_env` instead. +""" + +from ._base import read_env +from ._bool import read_bool +from ._enum import read_enum +from ._numeric import read_float, read_int +from ._str import read_str + +__all__ = [ + "read_bool", + "read_enum", + "read_env", + "read_float", + "read_int", + "read_str", +] diff --git a/src/rics/env/read/_base.py b/src/rics/env/read/_base.py new file mode 100644 index 00000000..54fa9e5f --- /dev/null +++ b/src/rics/env/read/_base.py @@ -0,0 +1,123 @@ +import os +from collections.abc import Callable +from typing import NamedTuple, overload + +from rics.types import T + + +@overload +def read_env( + var: str, + converter: Callable[[str], T], + default: T, + *, + strict: bool = True, + type_name: str | None = None, + split: None = None, + catch: tuple[type[Exception], ...] | None = None, +) -> T: ... + + +@overload +def read_env( + var: str, + converter: Callable[[str], T], + default: T, + *, + strict: bool = True, + type_name: str | None = None, + split: str, + catch: tuple[type[Exception], ...] | None = None, +) -> list[T]: ... + + +def read_env( + var: str, + converter: Callable[[str], T], + default: T, + *, + strict: bool = True, + type_name: str | None = None, + split: str | None = None, + catch: tuple[type[Exception], ...] | None = None, +) -> T | list[T]: + """Read and convert an environment variable. + + Args: + var: Variable to read. + converter: A callable ``(str) -> T``, where the argument is the environment variable value. + default: Default value to use when `var` is not set (or blank). + strict: If ``False``, always fall back to `default` instead of raising. + type_name: Used in error messages. Derive if ``None``. + split: Character to split on. Returns ``list[T]`` when set. + catch: Types to suppress when calling ``converter``. Default is ``(ValueError, TypeError)``. + + Returns: + A ``T`` value, or a list thereof (if `split` is set). + + Raises: + ValueError: If conversion fails. + + Notes: + If the `variable` key is unset or the value is empty, the `default` value is always returned. + """ + value = os.environ.get(var) + if value is None: + return [default] if split else default + + value = str(value).strip() # Just in case; should already be a string. + if value == "": + return [default] if split else default + + if catch is None: + catch = ValueError, TypeError + + cause: BaseException + reason: str + if split is None: + try: + return converter(value) + except catch as exc: + cause = exc + reason = f": {exc}" + else: + result = _split(value.split(split), converter, catch) + if isinstance(result, _ExceptionDetails): + reason = f".\nNOTE: Failed at {var}[{result.idx}]={result.value!r}: {result.exception}" + cause = result.exception + else: + return result + + if strict: + if type_name is None: + type_name = type(default).__name__ + if split: + type_name = f"list[{type_name}]" + msg = f"Bad value {var}={value!r}; not a valid `{type_name}` value" + reason + raise ValueError(msg) from cause + + return [default] if split else default + + +class _ExceptionDetails(NamedTuple): + exception: BaseException + idx: int + value: str + + +def _split( + values: list[str], + converter: Callable[[str], T], + catch: tuple[type[BaseException], ...], +) -> list[T] | _ExceptionDetails: + items = [] + for i, value in enumerate(values): + stripped = value.strip() + if stripped: + try: + result = converter(stripped) + items.append(result) + except catch as exc: + return _ExceptionDetails(exc, i, stripped) + + return items diff --git a/src/rics/env/read/_bool.py b/src/rics/env/read/_bool.py new file mode 100644 index 00000000..9c3ee379 --- /dev/null +++ b/src/rics/env/read/_bool.py @@ -0,0 +1,54 @@ +from typing import overload + +from rics.strings import str_as_bool + +from ._base import read_env + + +@overload +def read_bool(var: str, default: bool = False, *, strict: bool = True, split: str) -> list[bool]: ... +@overload +def read_bool(var: str, default: bool = False, *, strict: bool = True, split: None = None) -> bool: ... +def read_bool(var: str, default: bool = False, *, strict: bool = True, split: str | None = None) -> bool | list[bool]: + """Read ``bool`` variable. + + Args: + var: Variable to read. + default: Default value to use when `var` is not set (or blank). + strict: If ``False``, always fall back to `default` instead of raising. + split: Character to split on. Returns ``list[bool]`` when set. + + Returns: + A ``bool`` value, or a list thereof (if `split` is set). + + Notes: + See :func:`~rics.strings.str_as_bool` for value mapping. + + Examples: + Basic usage. + + >>> import os + >>> os.environ["MY_BOOL"] = "true" + >>> read_bool("MY_BOOL") + True + + >>> os.environ["MY_BOOL"] = "0" + >>> read_bool("MY_BOOL") + False + + When using ``strict=False``, unmapped values are converted to the `default` instead of raising. + + >>> os.environ["MY_BOOL"] = "not-a-bool" + >>> read_bool("MY_BOOL", default=True, strict=False) + True + + When using `split`, elements are cleaned individually and blank items are skipped. + + >>> os.environ["MY_BOOL_LIST"] = "true, 0, no, yes, enable,, false" + >>> read_bool("MY_BOOL_LIST", split=",") + [True, False, False, True, True, False] + + Conversion (see :attr:`~rics.strings.str_as_bool`) must succeed for all elements, or the `default` will be + returned. + """ + return read_env(var, str_as_bool, default, strict=strict, split=split) diff --git a/src/rics/env/read/_enum.py b/src/rics/env/read/_enum.py new file mode 100644 index 00000000..b3de3c0d --- /dev/null +++ b/src/rics/env/read/_enum.py @@ -0,0 +1,73 @@ +from typing import TYPE_CHECKING, overload + +if TYPE_CHECKING: + from rics.types import EnumT + + +@overload +def read_enum(var: str, default_or_type: "EnumT | type[EnumT]", *, split: str) -> list["EnumT"]: ... +@overload +def read_enum(var: str, default_or_type: "EnumT | type[EnumT]", *, split: None = None) -> "EnumT": ... +def read_enum(var: str, default_or_type: "EnumT | type[EnumT]", *, split: str | None = None) -> "EnumT | list[EnumT]": + """Read ``enum`` variable. + + This function wraps :meth:`.LiteralHelper.from_enum` and :meth:`.LiteralHelper.read_env`. + + Args: + var: Variable to read. + default_or_type: Enum type or default enum value. Must be a concrete value (e.g. ``Animal.dog`` instead of just + ``Animal``) to allow unset or blank `var` values. + split: Character to split on. Returns ``list[EnumT]`` when set. + + Returns: + An :attr:`~rics.types.EnumT` value, or a list thereof (if `split` is set). + + Raises: + TypeError: If conversion fails. + + Examples: + Basic usage. + + >>> from enum import Enum + >>> class Animal(Enum): + ... cat = "meow" + ... dog = "woof" + + Enums are matched by name, never by value. + + >>> import os + >>> os.environ["MY_ANIMAL"] = "cat" + >>> read_enum("MY_ANIMAL", Animal) + + + Input is always cleaned and normalized. + + >>> os.environ["MY_ANIMAL"] = " DOG" + >>> read_enum("MY_ANIMAL", Animal) + + + This also applies when reading multiple enums. + + >>> os.environ["MY_ANIMAL_LIST"] = " DOG, cat , CAT" + >>> read_enum("MY_ANIMAL_LIST", Animal, split=",") + [, , ] + + + Unlike the other functions in this module, ``read_enum`` always runs in `strict` mode. Define a default for when + the `var` is not set or blank by passing concrete value in the second position. + + >>> read_enum("MISSING_VARIABLE", Animal.dog) + + + Unknown values will always raise. + """ + from rics.types import LiteralHelper + + if isinstance(default_or_type, type): + cls = default_or_type + default = None + else: + cls = type(default_or_type) + default = default_or_type + + return LiteralHelper.from_enum(cls).read_env(var, default=default, split=split) diff --git a/src/rics/env/read/_numeric.py b/src/rics/env/read/_numeric.py new file mode 100644 index 00000000..55b0296d --- /dev/null +++ b/src/rics/env/read/_numeric.py @@ -0,0 +1,91 @@ +from typing import overload + +from ._base import read_env + + +@overload +def read_int(var: str, default: int = 0, *, strict: bool = True, split: str) -> list[int]: ... +@overload +def read_int(var: str, default: int = 0, *, strict: bool = True, split: None = None) -> int: ... +def read_int(var: str, default: int = 0, *, strict: bool = True, split: str | None = None) -> int | list[int]: + """Read ``int`` variable. + + Args: + var: Variable to read. + default: Default value to use when `var` is not set (or blank). + strict: If ``False``, always fall back to `default` instead of raising. + split: Character to split on. Returns ``list[int]`` when set. + + Returns: + An ``int`` value, or a list thereof (if `split` is set). + + Examples: + Basic usage. + + >>> import os + >>> os.environ["MY_INT"] = "1" + >>> read_int("MY_INT") + 1 + + When using ``strict=False``, unmapped values are converted to the `default` instead of raising. + + >>> os.environ["MY_INT"] = "not-an-int" + >>> read_int("MY_INT", strict=False) + 0 + + Note that this includes passing ``float`` values, even if the decimal is zero. + + >>> os.environ["MY_INT"] = "0.0" + >>> read_int("MY_INT", default=2019, strict=False) + 2019 + + When using `split`, elements are cleaned individually and blank items are skipped. + + >>> os.environ["MY_INT_LIST"] = "0, 1, , 5" + >>> read_int("MY_INT_LIST", split=",") + [0, 1, 5] + + Conversion must succeed for all elements, or the `default` will be returned. + """ + return read_env(var, int, default, strict=strict, split=split, catch=(ValueError,)) + + +@overload +def read_float(var: str, default: float = 0.0, *, strict: bool = True, split: str) -> list[float]: ... +@overload +def read_float(var: str, default: float = 0.0, *, strict: bool = True, split: None = None) -> float: ... +def read_float(var: str, default: float = 0.0, *, strict: bool = True, split: str | None = None) -> float | list[float]: + """Read ``float`` variable. + + Args: + var: Variable to read. + default: Default value to use when `var` is not set (or blank). + strict: If ``False``, always fall back to `default` instead of raising. + split: Character to split on. Returns ``list[float]`` when set. + + Returns: + An ``float`` value, or a list thereof (if `split` is set). + + Examples: + Basic usage. + + >>> import os + >>> os.environ["MY_FLOAT"] = "1.0" + >>> read_float("MY_FLOAT") + 1.0 + + When using ``strict=False``, unmapped values are converted to the `default` instead of raising. + + >>> os.environ["MY_FLOAT"] = "not-a-float" + >>> read_float("MY_FLOAT", strict=False) + 0.0 + + When using `split`, elements are cleaned individually and blank items are skipped. + + >>> os.environ["MY_FLOAT_LIST"] = "0, 1.1, , 5" + >>> read_float("MY_FLOAT_LIST", split=",") + [0.0, 1.1, 5.0] + + Conversion must succeed for all elements, or the `default` will be returned. + """ + return read_env(var, float, default, strict=strict, split=split, catch=(ValueError,)) diff --git a/src/rics/env/read/_str.py b/src/rics/env/read/_str.py new file mode 100644 index 00000000..513ed364 --- /dev/null +++ b/src/rics/env/read/_str.py @@ -0,0 +1,38 @@ +from typing import overload + +from ._base import read_env + + +@overload +def read_str(var: str, default: str = "", *, strict: bool = True, split: str) -> list[str]: ... +@overload +def read_str(var: str, default: str = "", *, strict: bool = True, split: None = None) -> str: ... +def read_str(var: str, default: str = "", *, strict: bool = True, split: str | None = None) -> str | list[str]: + """Read ``str`` variable. + + Args: + var: Variable to read. + default: Default value to use when `var` is not set (or blank). + strict: If ``False``, always fall back to `default` instead of raising. + split: Character to split on. Returns ``list[str]`` when set. + + Returns: + An ``str`` value, or a list thereof (if `split` is set). + + Examples: + Basic usage. + + >>> import os + >>> os.environ["MY_STR"] = " foo " + >>> read_str("MY_STR") + 'foo' + + Input is stripped and cleaned, returning bare strings. Empty items are discarded when using `split`. + + >>> os.environ["MY_STR_LIST"] = " foo, , bar " + >>> read_str("MY_STR_LIST", split=",") + ['foo', 'bar'] + + This means that input such as ``', , , '`` will return an empty list. + """ + return read_env(var, str.strip, default, strict=strict, split=split) diff --git a/src/rics/envinterp/__init__.py b/src/rics/envinterp/__init__.py index 3beed833..1942f422 100644 --- a/src/rics/envinterp/__init__.py +++ b/src/rics/envinterp/__init__.py @@ -1,70 +1,19 @@ -"""Environment variable interpolation in files and strings. +"""Legacy module. -This module provides utilities to read text and replace environment variable references with the actual environment -variable value using a familiar syntax. Optional default values are also possible, as well as recursively nested -variables (must be explicitly enabled). +Schedule: + * ``rics==0.6.0``: Emit :py:class:`DeprecationWarning` on import. + * ``rics==0.7.0``: Drop legacy module. -Syntax: - Similar to Bash variable interpolation; ``${}``, where ```` is the name of the environment variable you - wish to extract. This particular form will raise an exception if ```` is not set. - -Default values: - Default values are specified by stating the default value after a colon; ``${:default-value}``. The default - value may be blank, in which case an empty string is used as the default. - -There are three main ways of using this module: - - * The :meth:`.Variable.parse_string`-function, combined with :meth:`.Variable.get_value`. This gives you the most - amount of control. - * The :func:`replace_in_string`-function, for basic interpolation without recursion in an entire string. - * The :func:`rics.misc.interpolate_environment_variables`-function, which adds some additional logic on top of what - the :class:`Variable`-methods provide. - -Examples: - We'll set ``ENV_VAR0=VALUE0`` and ``ENV_VAR1=VALUE1``. Vars 2 and 4 are not set. - - >>> from os import environ - >>> environ["ENV_VAR0"] = "VALUE0" - >>> environ["ENV_VAR1"] = "VALUE1" - - Basic interpolation. - - >>> Variable.parse_first("${ENV_VAR0}").get_value() - 'VALUE0' - - Setting a default works as expected. The default may also be empty. - - >>> Variable.parse_first("${ENV_VAR2:default}").get_value() - 'default' - >>> Variable.parse_first("${ENV_VAR2:}").get_value() - '' - - The variable must exist if no default is given. - - >>> Variable.parse_first("${DOESNT_EXIST}").get_value() - Traceback (most recent call last): - File "/git/rics/.venv/lib/python3.11/site-packages/IPython/core/interactiveshell.py", line 3433, in run_code - exec(code_obj, self.user_global_ns, self.user_ns) - File "", line 1, in - Variable.parse_first("${DOESNT_EXIST}").get_value() - File "/git/rics/src/rics/envinterp/_variable.py", line 72, in get_value - raise UnsetVariableError(self.name) - rics.envinterp._variable.UnsetVariableError: Required Environment Variable 'DOESNT_EXIST': Not set. - - Furthermore, variables may be nested, but this requires setting the flag to enable recursive parsing. - - >>> Variable.parse_first("${ ENV_VAR2 :${ ENV_VAR0 }}").get_value( - ... resolve_nested_defaults=True - ... ) - 'VALUE0' - - Whitespaced around the names is fine. Finally, nesting may be arbitrarily deep. - - >>> Variable.parse_first("${ENV_VAR3:${ENV_VAR4:${ENV_VAR0}}}").get_value(True) - 'VALUE0' +Please use :mod:`rics.env.interpolation` instead. """ -from ._file_utils import replace_in_string -from ._variable import UnsetVariableError, Variable +from rics.env.interpolation import UnsetVariableError, Variable, replace_in_string __all__ = ["UnsetVariableError", "Variable", "replace_in_string"] + +import os + +if os.environ.get("SPHINX_BUILD") == "true": + # Ugly fix for duplicate indexes. + del __all__, UnsetVariableError, Variable, replace_in_string +del os diff --git a/src/rics/envinterp/_file_utils.py b/src/rics/envinterp/_file_utils.py deleted file mode 100644 index 857c860a..00000000 --- a/src/rics/envinterp/_file_utils.py +++ /dev/null @@ -1,20 +0,0 @@ -from ._variable import Variable - - -def replace_in_string(s: str) -> str: - """Replace environment variable names with their values in `s`. - - Recursive variable interpolation is not supported by this function. - - Args: - s: A string. - - Returns: - A copy of `s` with the env vars names found within their values. - - Raises: - UnsetVariableError: If any variables are unset with no default specified. - """ - for var in Variable.parse_string(s): - s = s.replace(var.full_match, var.get_value()) - return s diff --git a/src/rics/misc.py b/src/rics/misc.py index 851b36b7..685446b1 100644 --- a/src/rics/misc.py +++ b/src/rics/misc.py @@ -7,8 +7,6 @@ from types import ModuleType as _ModuleType from . import paths as _paths -from .envinterp import UnsetVariableError as _UnsetVariableError -from .envinterp import Variable as _Variable from .types import AnyPath as _AnyPath @@ -18,40 +16,10 @@ def interpolate_environment_variables( allow_nested: bool = True, allow_blank: bool = False, ) -> str: - """Interpolate environment variables in a string `s`. + """Alias of :func:`rics.env.interpolation.replace_in_string`.""" + from rics.env.interpolation import replace_in_string - This function replaces references to environment variables with the actual value of the variable, or a default if - specified. The syntax is similar to Bash string interpolation; use ``${}`` for mandatory variables, and - ``${:default}`` for optional variables. - - Args: - s: A string in which to interpolate. - allow_blank: If ``True``, allow variables to be set but empty. - allow_nested: If ``True`` allow using another environment variable as the default value. This option will not - verify whether the actual values are interpolation-strings. - - Returns: - A copy of `s`, after environment variable interpolation. - - Raises: - ValueError: If nested variables are discovered (only when ``allow_nested=False``). - UnsetVariableError: If any required environment variables are unset or blank (only when ``allow_blank=False``). - - See Also: - The :mod:`rics.envinterp` module, which this function wraps. - - """ - for var in _Variable.parse_string(s): - if not allow_nested and (var.default and _Variable.parse_string(var.default)): - raise ValueError(f"Nested variables forbidden since {allow_nested=}.") - - value = var.get_value(resolve_nested_defaults=allow_nested).strip() - - if not (allow_blank or value): - raise _UnsetVariableError(var.name, f"Empty values forbidden since {allow_blank=}.") - - s = s.replace(var.full_match, value) - return s + return replace_in_string(s, allow_nested=allow_nested, allow_blank=allow_blank) GBFNReturnType = _t.TypeVar("GBFNReturnType") diff --git a/src/rics/strings.py b/src/rics/strings.py index c2234f2f..d1efcf12 100644 --- a/src/rics/strings.py +++ b/src/rics/strings.py @@ -271,3 +271,63 @@ def snake_to_camel(s: str, *, lower: bool = True) -> str: if lower: s0 = s0.lower() return s0 + s[1:] + + +TRUE = "1", "true", "yes", "on", "enable", "enabled" +FALSE = "0", "false", "no", "off", "disable", "disabled" + + +def str_as_bool(s: str) -> bool: + """Convert a string `s` to a boolean value. + + The output is determined by the content of `s`, as per the mapping shown below. + + Keys: + * False: ``{false}`` + * True: ``{true}`` + + Matching is case-insensitive. + + Args: + s: A string. + + Returns: + A ``bool`` value. + + Raises: + TypeError: If `s` is not a string. + ValueError: If `s` cannot be converted to ``bool`` using the keys above. + + Examples: + Basic usage. + + >>> str_as_bool("true"), str_as_bool("false") + (True, False) + + The input is cleaned and normalized. + + >>> str_as_bool(" TRUE"), str_as_bool("False") + (True, False) + + Input strings are normalized using :py:meth:`str.strip` and :py:meth:`str.lower`. + + Notes: + Using ``bool()`` is equivalent to ``len() == 0``. + """ + if not isinstance(s, str): + msg = f"Input must be a string; got {type(s).__name__}." + raise TypeError(msg) + + s = s.strip().lower() + if s in FALSE: + return False + if s in TRUE: + return True + + error = ValueError(f"Cannot cast {s!r} to `bool`.") + error.add_note(f"{FALSE=}") + error.add_note(f"{TRUE=}") + raise error + + +str_as_bool.__doc__ = str_as_bool.__doc__.format(false=FALSE, true=TRUE) # type: ignore[union-attr] diff --git a/src/rics/types.py b/src/rics/types.py index d556677d..efddbcf8 100644 --- a/src/rics/types.py +++ b/src/rics/types.py @@ -55,26 +55,16 @@ def verify_enum( TypeError: Bad response='x'; expected a Response enum option: Response.Yes | Response.No). Notes: - This function wraps :class:`LiteralHelper`. + This function wraps :meth:`LiteralHelper.from_enum`. """ - if name is None: - from rics.strings import camel_to_snake - - name = camel_to_snake(enum_type.__name__) - - return LiteralHelper( - literal_type=enum_type, - default_name=name, - type_name=None, - normalizer=lambda arg: arg.name.lower() if isinstance(arg, _Enum) else str(arg).strip().lower(), - ).check(value) + return LiteralHelper.from_enum(enum_type, default_name=name).check(value) def verify_literal( value: _t.Any, literal_type: _abc.Collection[T] | type[T] | _t.Any, # Runtime type is typically . - name: str = "value", *, + name: str = "value", type_name: str | None = None, exc_type: type[Exception] | None = None, normalizer: _abc.Callable[[_t.Any], _t.Any] | None = None, @@ -169,7 +159,7 @@ def __init__( *, default_name: str = "value", type_name: str | None = None, - exc_type: type[Exception] | None = None, + exc_type: type[BaseException] | None = None, normalizer: _abc.Callable[[_t.Any | T], _t.Any] | None = None, ) -> None: options = self._extract_options(literal_type) @@ -178,10 +168,72 @@ def __init__( self._name = default_name self._type_name = type_name - self._exc_type = TypeError if exc_type is None else exc_type + self._exc_type: type[BaseException] = TypeError if exc_type is None else exc_type self._options = options self._normalizer = normalizer + @classmethod + def from_enum(cls, enum_type: type[EnumT], *, default_name: str | None = None) -> "LiteralHelper[EnumT]": + """Construct helper for an :class:`enum.Enum` type. + + Args: + enum_type: Desired enum type. + default_name: Default name of the user-facing argument. Used in error messages. Derive if ``None``. + + Returns: + A ``LiteralHelper`` constructor. + """ + if default_name is None: + from rics.strings import camel_to_snake + + default_name = camel_to_snake(enum_type.__name__) + + return LiteralHelper( + literal_type=enum_type, + default_name=default_name, + type_name=None, # Derived and pretty-printed on failure if None. + normalizer=cls._enum_normalizer, + ) + + @_t.overload + def read_env(self, variable: str, *, default: T | None, split: str) -> list[T]: ... + @_t.overload + def read_env(self, variable: str, *, default: T | None, split: None = None) -> T: ... + def read_env(self, variable: str, *, default: T | None, split: str | None = None) -> T | list[T]: + """Read environment value. + + Args: + variable: Environment variable name. + default: Value to use is `variable` is not set or blank. Pass ``None`` to raise. + split: Character to split on. Returns ``list[T]`` when set. + + Returns: + A :attr:`T`, or a list thereof (if `split` is set). + + Notes: + Use the :mod:`rics.env.read` functions to read primitive types such as booleans. + """ + import os + + value = os.environ.get(variable, "").strip() + if default is None: + if variable not in os.environ: + self._raise("", variable) + elif not value: + self._raise("", variable) + elif not value: + return default if split is None else [default] + + if split is None: + return self.check(value, name=variable) + + values = value.split(split) + return [self.check(value, name=f"{variable}[{i}]") for i, value in enumerate(map(str.strip, values)) if value] + + @classmethod + def _enum_normalizer(cls, arg: _t.Any) -> str: + return arg.name.lower() if isinstance(arg, _Enum) else str(arg).strip().lower() + @property def options(self) -> tuple[T, ...]: """Permitted options as explicit values.""" @@ -195,7 +247,7 @@ def check(self, value: _t.Any, name: str | None = None) -> T: name: Name to use for this call. Returns: - A valid ``Literal`` value. + A valid value. """ name = self._name if name is None else name options = self._options @@ -210,6 +262,13 @@ def check(self, value: _t.Any, name: str | None = None) -> T: if normalizer(option) == normalized_value: return option + self._raise(repr(value), name) + + __call__ = check + + def _raise(self, value: str, name: str) -> _t.Never: + options = self._options + if self._type_name is None: if isinstance(options[0], _Enum): pretty_options = f"a {type(options[0]).__name__} enum option: {{ {' | '.join(map(str, options))} }}" @@ -218,11 +277,9 @@ def check(self, value: _t.Any, name: str | None = None) -> T: else: pretty_options = f"a {self._type_name}[{', '.join(map(repr, options))}]" - msg = f"Bad {name}={value!r}; expected {pretty_options}." + msg = f"Bad {name}={value}; expected {pretty_options}." raise self._exc_type(msg) - __call__ = check - @classmethod def _extract_options(cls, literal_type: _abc.Collection[T] | _t.Any) -> tuple[T, ...]: if isinstance(literal_type, type(_t.Literal[None])): diff --git a/tests/env/__init__.py b/tests/env/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/env/interpolation/__init__.py b/tests/env/interpolation/__init__.py new file mode 100644 index 00000000..d6f940c8 --- /dev/null +++ b/tests/env/interpolation/__init__.py @@ -0,0 +1,10 @@ +def setup_module(): + from tests.env.interpolation.conftest import set_variables + + set_variables() + + +def teardown_module(): + from tests.env.interpolation.conftest import set_variables + + set_variables() diff --git a/tests/envinterp/conftest.py b/tests/env/interpolation/conftest.py similarity index 100% rename from tests/envinterp/conftest.py rename to tests/env/interpolation/conftest.py diff --git a/tests/envinterp/test_basics.py b/tests/env/interpolation/test_basics.py similarity index 89% rename from tests/envinterp/test_basics.py rename to tests/env/interpolation/test_basics.py index e4e01c56..cf8cb3d0 100644 --- a/tests/envinterp/test_basics.py +++ b/tests/env/interpolation/test_basics.py @@ -1,7 +1,7 @@ import pytest -from rics.envinterp import UnsetVariableError, Variable -from tests.envinterp.conftest import set_variables +from rics.env.interpolation import UnsetVariableError, Variable +from tests.env.interpolation.conftest import set_variables VARIABLES = [ Variable("ENV_VAR0", None, "${ENV_VAR0}"), diff --git a/tests/envinterp/test_nested.py b/tests/env/interpolation/test_nested.py similarity index 93% rename from tests/envinterp/test_nested.py rename to tests/env/interpolation/test_nested.py index 8fd09ce5..ab13b533 100644 --- a/tests/envinterp/test_nested.py +++ b/tests/env/interpolation/test_nested.py @@ -2,8 +2,8 @@ import pytest -from rics.envinterp import UnsetVariableError, Variable -from tests.envinterp.conftest import set_variables +from rics.env.interpolation import UnsetVariableError, Variable +from tests.env.interpolation.conftest import set_variables ExcType = TypeVar("ExcType", bound=Exception) diff --git a/tests/envinterp/test_wrapper.py b/tests/env/interpolation/test_wrapper.py similarity index 87% rename from tests/envinterp/test_wrapper.py rename to tests/env/interpolation/test_wrapper.py index 5906c1dd..ef631426 100644 --- a/tests/envinterp/test_wrapper.py +++ b/tests/env/interpolation/test_wrapper.py @@ -2,8 +2,7 @@ import pytest -from rics import misc -from rics.envinterp import UnsetVariableError +from rics.env.interpolation import UnsetVariableError, replace_in_string @pytest.mark.parametrize( @@ -20,7 +19,7 @@ def test_interpolate_environment_variables_default_args(s, expected): os.environ["ENV_VAR0"] = "VALUE0" os.environ["ENV_VAR1"] = "VALUE1" - assert misc.interpolate_environment_variables(s) == expected + assert replace_in_string(s) == expected @pytest.mark.parametrize( @@ -41,7 +40,7 @@ def test_interpolate_environment_variables(s, expected, kwargs): os.environ["HACKY"] = "${NESTED}" try: - actual = misc.interpolate_environment_variables(s, **kwargs) + actual = replace_in_string(s, **kwargs) except (ValueError, UnsetVariableError, NotImplementedError) as e: actual = e # type: ignore diff --git a/tests/env/read/__init__.py b/tests/env/read/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/env/read/test_bool.py b/tests/env/read/test_bool.py new file mode 100644 index 00000000..c7270c87 --- /dev/null +++ b/tests/env/read/test_bool.py @@ -0,0 +1,76 @@ +import pytest + +from rics.env.read import read_bool + +EXPLICIT = [ + # False values + ("0", False), + ("false", False), + ("FALSE", False), + ("False", False), + ("no", False), + ("disabled", False), + ("off", False), + # True values + ("1", True), + ("true", True), + ("TRUE", True), + ("True", True), + ("yes", True), + ("enabled", True), + ("on", True), +] +VAR = "MY_BOOL" + + +@pytest.mark.parametrize("value, expected", [*EXPLICIT, ("junk", True), ("", True)]) +def test_default_true(monkeypatch, value, expected): + monkeypatch.setenv(VAR, value) + + actual = read_bool(VAR, default=True, strict=False) + assert actual == expected + + +@pytest.mark.parametrize("value, expected", [*EXPLICIT, ("junk", False), ("", False)]) +def test_default_false(monkeypatch, value, expected): + monkeypatch.setenv(VAR, value) + + actual = read_bool(VAR, default=False, strict=False) + assert actual == expected + + +@pytest.mark.parametrize("value, expected", [*EXPLICIT, ("junk", None), ("", False)]) +def test_strict(monkeypatch, value, expected): + monkeypatch.setenv(VAR, value) + + if expected is None: + match = "Bad value MY_BOOL='junk'; not a valid `bool` value: Cannot cast 'junk' to `bool`." + with pytest.raises(ValueError, match=match): + read_bool(VAR, strict=True) + else: + actual = read_bool(VAR, strict=True) + assert actual == expected + + +class TestList: + def test_bad_item_strict_false(self, monkeypatch): + monkeypatch.setenv(VAR, "true, not-a-bool") + actual = read_bool(VAR, split=",", strict=False) + assert actual == [False] + + def test_bad_item_strict_true(self, monkeypatch): + monkeypatch.setenv(VAR, "true, not-a-bool") + + with pytest.raises(ValueError) as exc_info: + read_bool(VAR, split=",") + + expected = ( + "Bad value MY_BOOL='true, not-a-bool'; not a valid `list[bool]` value." + "\nNOTE: Failed at MY_BOOL[1]='not-a-bool': Cannot cast 'not-a-bool' to `bool`." + ) + assert str(exc_info.value) == expected + + def test_list(self, monkeypatch): + monkeypatch.setenv(VAR, "true, 0, false , 1, yes, no, on, off") + actual = read_bool(VAR, split=",") + assert actual == [True, False, False, True, True, False, True, False] diff --git a/tests/env/read/test_numeric.py b/tests/env/read/test_numeric.py new file mode 100644 index 00000000..b80df6d0 --- /dev/null +++ b/tests/env/read/test_numeric.py @@ -0,0 +1,74 @@ +from math import isnan + +import pytest + +from rics.env.read import read_float, read_int + + +@pytest.mark.parametrize( + "value, expected", + [ + ("-1", -1), + ("0", 0), + ("", 0), + ("1", 1), + ], +) +def test_int(monkeypatch, value, expected): + assert isinstance(expected, int) + name = "MY_VAR" + monkeypatch.setenv(name, value) + + actual = read_int(name, strict=False) + assert isinstance(actual, int) + assert actual == expected + + +@pytest.mark.parametrize( + "value, expected", + [ + ("-1", -1.0), + ("-1.0", -1.0), + ("0", 0.0), + ("00.000", 0.0), + ("", 0.0), + ("1", 1.0), + ("1.23", 1.23), + ], +) +def test_float(monkeypatch, value, expected): + assert isinstance(expected, float) + + name = "MY_VAR" + monkeypatch.setenv(name, value) + + actual = read_float(name, strict=False) + assert isinstance(actual, float) + assert actual == expected + + +def test_nan(monkeypatch): + name = "MY_VAR" + monkeypatch.setenv(name, "nan") + + actual = read_float(name, strict=False) + assert isinstance(actual, float) + assert isnan(actual) + + +@pytest.mark.parametrize("value", ["dsadasdas", "1.2", "nan"]) +def test_strict_int(monkeypatch, value): + name = "MY_VAR" + monkeypatch.setenv(name, value) + + with pytest.raises(ValueError, match="not a valid `int` value"): + read_int(name) + + +@pytest.mark.parametrize("value", ["dsadasdas", "false", "true"]) +def test_strict_float(monkeypatch, value): + name = "MY_VAR" + monkeypatch.setenv(name, value) + + with pytest.raises(ValueError, match="not a valid `float` value"): + read_float(name) diff --git a/tests/env/read/test_str.py b/tests/env/read/test_str.py new file mode 100644 index 00000000..ba91ac23 --- /dev/null +++ b/tests/env/read/test_str.py @@ -0,0 +1,25 @@ +import pytest + +from rics.env.read import read_str + +VAR = "MY_STR" + + +def test_blank_parts(monkeypatch): + monkeypatch.setenv(VAR, ", , , ") # From the docstring + assert read_str(VAR, split=",") == [] + + +def test_parts_are_stripped(monkeypatch): + monkeypatch.setenv(VAR, ", aa , b, ") + assert read_str(VAR, split=",") == ["aa", "b"] + + +class TestDefaultOnly: + @pytest.mark.parametrize("value", [" ", "", None]) + def test_unset(self, monkeypatch, value): + if value is None: + monkeypatch.delenv(VAR, raising=False) + else: + monkeypatch.setenv(VAR, value) + assert read_str(VAR, default="some-default") == "some-default" diff --git a/tests/envinterp/__init__.py b/tests/envinterp/__init__.py deleted file mode 100644 index 919e67e3..00000000 --- a/tests/envinterp/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -def setup_module(): - from tests.envinterp.conftest import set_variables - - set_variables() - - -def teardown_module(): - from tests.envinterp.conftest import set_variables - - set_variables() diff --git a/tests/envinterp/test_file_utils.py b/tests/envinterp/test_file_utils.py deleted file mode 100644 index 706a5adc..00000000 --- a/tests/envinterp/test_file_utils.py +++ /dev/null @@ -1,44 +0,0 @@ -import pytest - -from rics.envinterp import replace_in_string -from tests.envinterp.conftest import set_variables - - -@pytest.fixture(scope="session") -def expected(): - yield """ - k0 = "${ENV_VAR0}" # If ENV_VAR0 does not exist, crash. - k1 = "${ENV_VAR1:}" # If ENV_VAR1 does not exist, delete the 'k1'-key. - k2 = "${ENV_VAR2:default}" # If ENV_VAR2 does not exist, replace it with 'default' - k3 = "${ENV_VAR3?bad-syntax!}" - """ - - -def test_all_set(expected): - set_variables(0, 1, 2, 3) - - actual = replace_in_string(expected) - assert ( - actual - == """ - k0 = "VALUE0" # If ENV_VAR0 does not exist, crash. - k1 = "VALUE1" # If ENV_VAR1 does not exist, delete the 'k1'-key. - k2 = "VALUE2" # If ENV_VAR2 does not exist, replace it with 'default' - k3 = "${ENV_VAR3?bad-syntax!}" - """ - ) - - -def test_required_set(expected): - set_variables(0, 3) - - actual = replace_in_string(expected) - assert ( - actual - == """ - k0 = "VALUE0" # If ENV_VAR0 does not exist, crash. - k1 = "" # If ENV_VAR1 does not exist, delete the 'k1'-key. - k2 = "default" # If ENV_VAR2 does not exist, replace it with 'default' - k3 = "${ENV_VAR3?bad-syntax!}" - """ - ) diff --git a/tests/test_types/test_verify.py b/tests/test_types/test_verify.py index 01878d15..afa228b3 100644 --- a/tests/test_types/test_verify.py +++ b/tests/test_types/test_verify.py @@ -70,3 +70,101 @@ class TestLiteralUnions: def test_deduplicate(self): actual = LiteralHelper(UnionLiteral)._options # type: ignore[var-annotated] assert actual == ("a", "b", "c") + + +class FromEnvBase: + VAR_NAME: str + + @classmethod + def setup_helper(cls, monkeypatch: pytest.MonkeyPatch, value: str | None) -> LiteralHelper[AOrB]: + if value is None: + monkeypatch.delenv(cls.VAR_NAME, raising=False) + else: + monkeypatch.setenv(cls.VAR_NAME, value) + + return LiteralHelper.from_enum(AOrB) + + +class TestFromEnv(FromEnvBase): + VAR_NAME = "TestFromEnv" + + @pytest.mark.parametrize( + "value, expected", + [ + ("b", AOrB.b), + ("B", AOrB.b), + (" B ", AOrB.b), + ], + ) + def test_read(self, monkeypatch, value, expected): + helper = self.setup_helper(monkeypatch, value) + actual = helper.read_env(self.VAR_NAME, default=None) + assert actual == expected + + @pytest.mark.parametrize( + "value, err_value", + [ + (None, ""), + ("c", "'c'"), + ("", ""), + (" ", ""), + ], + ) + def test_error(self, monkeypatch, value, err_value): + expected = f"Bad TestFromEnv={err_value}; expected a AOrB enum option: {{ AOrB.a | AOrB.b }}." + helper = self.setup_helper(monkeypatch, value) + + with pytest.raises(TypeError) as exc_info: + helper.read_env(self.VAR_NAME, default=None) + + assert str(exc_info.value) == expected + + @pytest.mark.parametrize("value", [None, "", " "]) + @pytest.mark.parametrize("default", [AOrB.b, AOrB.b]) + def test_default(self, monkeypatch, value, default): + helper = self.setup_helper(monkeypatch, value) + actual = helper.read_env(self.VAR_NAME, default=default) + assert actual == default + + +class TestFromEnvList(FromEnvBase): + VAR_NAME = "TestFromEnvList" + + @pytest.mark.parametrize( + "value, expected", + [ + ("b, B", [AOrB.b, AOrB.b]), + ("B,a", [AOrB.b, AOrB.a]), + (" B ", [AOrB.b]), + ], + ) + def test_read(self, monkeypatch, value, expected): + helper = self.setup_helper(monkeypatch, value) + actual = helper.read_env(self.VAR_NAME, default=None, split=",") + assert actual == expected + + @pytest.mark.parametrize("value", [None, "", " "]) + @pytest.mark.parametrize("default", [AOrB.b, AOrB.b]) + def test_default(self, monkeypatch, value, default): + helper = self.setup_helper(monkeypatch, value) + actual = helper.read_env(self.VAR_NAME, default=default, split=",") + assert actual == [default] + + @pytest.mark.parametrize( + "value, err_value", + [ + (None, "TestFromEnvList="), + ("c", "TestFromEnvList[0]='c'"), + ("a, c, b", "TestFromEnvList[1]='c'"), + ("", "TestFromEnvList="), + (" ", "TestFromEnvList="), + ], + ) + def test_error(self, monkeypatch, value, err_value): + expected = f"Bad {err_value}; expected a AOrB enum option: {{ AOrB.a | AOrB.b }}." + helper = self.setup_helper(monkeypatch, value) + + with pytest.raises(TypeError) as exc_info: + helper.read_env(self.VAR_NAME, default=None, split=",") + + assert str(exc_info.value) == expected