diff --git a/scripts/populate_tox/config.py b/scripts/populate_tox/config.py index 1e1618bedb..9c891428bd 100644 --- a/scripts/populate_tox/config.py +++ b/scripts/populate_tox/config.py @@ -471,6 +471,7 @@ # The default "addr" value was removed in https://github.com/cunla/fakeredis-py/commit/0441288fb22c8c191fc716b561e0001cf512abe5. # from fakeredis. ">=1.1.14": ["fakeredis<2.36.0"], + ">=2.0": ["rq-scheduler"], "<0.9": ["fakeredis<1.0", "redis<3.2.2"], ">=0.9,<0.14": ["fakeredis>=1.0,<1.7.4"], "py3.6,py3.7": ["fakeredis!=2.26.0"], diff --git a/scripts/populate_tox/package_dependencies.jsonl b/scripts/populate_tox/package_dependencies.jsonl index 4cb887d60c..3dab9bebb5 100644 --- a/scripts/populate_tox/package_dependencies.jsonl +++ b/scripts/populate_tox/package_dependencies.jsonl @@ -636,3 +636,7 @@ {"python_version": "3.10", "dependencies": [{"metadata": {"name": "litellm", "version": "1.96.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/56/5d/b61da523ce16703c9a3bd415bd470f66679bb6f896b7420c395b8e0291a5/litellm-1.96.0-cp310-cp310-manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "httpx", "version": "0.28.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl"}}, {"metadata": {"name": "aiohttp", "version": "3.14.5"}, "download_info": {"url": "https://files.pythonhosted.org/packages/5d/38/01f5fc732d5373d6c099db0343279cd4e6607287d336bab6ff6ae9085a0d/aiohttp-3.14.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "async-timeout", "version": "5.0.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl"}}, {"metadata": {"name": "click", "version": "8.5.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl"}}, {"metadata": {"name": "fastuuid", "version": "0.14.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "httpcore", "version": "1.0.9"}, "download_info": {"url": "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl"}}, {"metadata": {"name": "importlib_metadata", "version": "8.9.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/7d/f9/97f2ca8bb3ec6e4b1d64f983ebe98b9a192faddff67fac3d6303a537e670/importlib_metadata-8.9.0-py3-none-any.whl"}}, {"metadata": {"name": "Jinja2", "version": "3.1.6"}, "download_info": {"url": "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl"}}, {"metadata": {"name": "jsonschema", "version": "4.26.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl"}}, {"metadata": {"name": "multidict", "version": "7.1.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/5a/9c/598d765e98aa02ff7204b6f11cf790e5840f5e8252d7b1d955ed0043bb94/multidict-7.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "openai", "version": "2.54.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/64/a8/bb76c7356de8ad57f59d5ff993d434df0607f07f08bcc9c9a5c275e399c0/openai-2.54.0-py3-none-any.whl"}}, {"metadata": {"name": "anyio", "version": "4.15.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl"}}, {"metadata": {"name": "distro", "version": "1.9.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl"}}, {"metadata": {"name": "jiter", "version": "0.17.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/93/2c/af9ca503b38683a82b6732352a861515f28e2d46fe445aea57e07fc86e06/jiter-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "pydantic", "version": "2.14.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl"}}, {"metadata": {"name": "pydantic_core", "version": "2.50.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/1b/1a/21a58791021397d9ca35295e3f4291c16e319155c5904334b89eb07e97e1/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "pydantic-settings", "version": "2.15.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/30/a4/2bffa9f8e804325a09867f0e9d30795c80ea9f8d62560bd1b6ad6220eb2f/pydantic_settings-2.15.0-py3-none-any.whl"}}, {"metadata": {"name": "python-dotenv", "version": "1.2.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/60/d1/38f3a3405989a89ac18390803e70c6ad7c7760da4f9b83cbeca0c44a0c72/python_dotenv-1.2.4-py3-none-any.whl"}}, {"metadata": {"name": "tiktoken", "version": "0.14.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/af/c8/051e7b72a816ff50eb34a1c7c5b185cd2429ffdf59a497baea35b2b6b2dd/tiktoken-0.14.0-cp310-cp310-manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "tokenizers", "version": "0.23.3"}, "download_info": {"url": "https://files.pythonhosted.org/packages/3a/40/6706b82693715581457c6d5423eaa7faae576bb0526c5738a57085eb4449/tokenizers-0.23.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "huggingface_hub", "version": "2.2.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/eb/b0/0f7b430fd100b3a3b037fdbb314878200241082e607b3383c63d91a13a72/huggingface_hub-2.2.0-py3-none-any.whl"}}, {"metadata": {"name": "hf-xet", "version": "1.7.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a3/4c/2bf3b66c215d409655f28de1622393dde04c9461280d48c7924bb3b2decd/hf_xet-1.7.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl"}}, {"metadata": {"name": "httpx2", "version": "2.13.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/d8/9c/6fe8931fd9f381042a9e4c7d5a7b4cbf7016b252bec0c99a49fce42c3326/httpx2-2.13.1-py3-none-any.whl"}}, {"metadata": {"name": "httpcore2", "version": "2.13.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/09/ba/a4568248771ce81957bfb7cc600264a40fbcda092391ee1c415c50be4bea/httpcore2-2.13.1-py3-none-any.whl"}}, {"metadata": {"name": "typing_extensions", "version": "4.16.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl"}}, {"metadata": {"name": "yarl", "version": "1.25.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/28/b9/7818ac6dec7fbcd16be2d19495807c01e2c38834e57d41c63356a75e786b/yarl-1.25.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "anthropic", "version": "1.13.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/ad/9a/a835340c91bc9a233bd54c05fc438fe07cbef71ca667f24f3892a268ae20/anthropic-1.13.0-py3-none-any.whl"}}, {"metadata": {"name": "docstring_parser", "version": "0.18.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl"}}, {"metadata": {"name": "sniffio", "version": "1.3.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl"}}, {"metadata": {"name": "google-genai", "version": "2.30.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/46/28/faa7c02d87df1343aa8e51c816694a82eb37f41d0e83253e6e75a00b96ec/google_genai-2.30.1-py3-none-any.whl"}}, {"metadata": {"name": "google-auth", "version": "2.62.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/eb/a3/87fe9b7203ec2e56ea6a57c75e6d632eaf9487c7f7978cdad1d801bee0af/google_auth-2.62.0-py3-none-any.whl"}}, {"metadata": {"name": "requests", "version": "2.34.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl"}}, {"metadata": {"name": "charset-normalizer", "version": "3.5.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/c4/9f/9f52d2886d52645987d603425482c63c5045a3005db1354a7097e5ed1ae9/charset_normalizer-3.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "idna", "version": "3.20"}, "download_info": {"url": "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl"}}, {"metadata": {"name": "tenacity", "version": "9.1.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl"}}, {"metadata": {"name": "urllib3", "version": "2.8.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl"}}, {"metadata": {"name": "websockets", "version": "16.1.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/f3/18/a17e2f0cde02dc10154c808deed7e1d8528afff93612f70d3f0a5b19b011/websockets-16.1.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}}, {"metadata": {"name": "pytest-asyncio", "version": "1.4.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl"}}, {"metadata": {"name": "backports.asyncio.runner", "version": "1.2.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl"}}, {"metadata": {"name": "pytest", "version": "9.1.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl"}}, {"metadata": {"name": "pluggy", "version": "1.6.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl"}}, {"metadata": {"name": "filelock", "version": "4.1.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/ee/86/032133892a5de43b5a98200b01aadcad68cc255e274a762f08b8a76d2912/filelock-4.1.0-py3-none-any.whl"}}, {"metadata": {"name": "aiohappyeyeballs", "version": "2.7.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl"}}, {"metadata": {"name": "aiosignal", "version": "1.4.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl"}}, {"metadata": {"name": "annotated-types", "version": "0.8.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl"}}, {"metadata": {"name": "attrs", "version": "26.1.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl"}}, {"metadata": {"name": "certifi", "version": "2026.7.22"}, "download_info": {"url": "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl"}}, {"metadata": {"name": "cryptography", "version": "50.0.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/40/cf/6b5c8e2fd9202d98988ab7cb5cc5c991704c4ad55f492ff408e4969f83f1/cryptography-50.0.2-cp39-abi3-manylinux_2_34_x86_64.whl"}}, {"metadata": {"name": "cffi", "version": "2.1.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl"}}, {"metadata": {"name": "exceptiongroup", "version": "1.3.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl"}}, {"metadata": {"name": "frozenlist", "version": "1.8.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}}, {"metadata": {"name": "fsspec", "version": "2026.9.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl"}}, {"metadata": {"name": "h11", "version": "0.16.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl"}}, {"metadata": {"name": "iniconfig", "version": "2.3.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl"}}, {"metadata": {"name": "jsonschema-specifications", "version": "2025.9.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl"}}, {"metadata": {"name": "MarkupSafe", "version": "3.0.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/00/26/c4708ed3b0f08e8e6d7cbce3314ac130cb5352d1f62951b6fef7878f2b1e/markupsafe-3.0.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "packaging", "version": "26.3"}, "download_info": {"url": "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl"}}, {"metadata": {"name": "propcache", "version": "0.5.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/f0/51/5adee15e12a7e314cece4543fbf295b0fe1b504dc78b32dff4c8eb1e29b0/propcache-0.5.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "pyasn1_modules", "version": "0.4.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl"}}, {"metadata": {"name": "pyasn1", "version": "0.6.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/9a/3b/6163796d69c3977d1e4287bea4a6979161cbbdd170ebb430511e8e1999ce/pyasn1-0.6.4-py3-none-any.whl"}}, {"metadata": {"name": "Pygments", "version": "2.21.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl"}}, {"metadata": {"name": "PyYAML", "version": "6.0.3"}, "download_info": {"url": "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "referencing", "version": "0.37.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl"}}, {"metadata": {"name": "rpds-py", "version": "0.30.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "tomli", "version": "2.5.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/60/3f/3e3f8fd0919249b0200c80fbc4f9a1e70be19f9883da71dfb7f8b9ab8aca/tomli-2.5.0-py3-none-any.whl"}}, {"metadata": {"name": "tqdm", "version": "4.70.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl"}}, {"metadata": {"name": "truststore", "version": "0.10.5"}, "download_info": {"url": "https://files.pythonhosted.org/packages/51/e9/3a7820be2bb0fe53b6bc9c3be26d3d1158004e4c3ab953aa6840b955b1e9/truststore-0.10.5-py3-none-any.whl"}}, {"metadata": {"name": "typing-inspection", "version": "0.4.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl"}}, {"metadata": {"name": "zipp", "version": "4.1.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/b5/98/df615823cd9419131ce19fba00de53a663794369e198aade064a244b385d/zipp-4.1.1-py3-none-any.whl"}}, {"metadata": {"name": "pycparser", "version": "3.11"}, "download_info": {"url": "https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl"}}, {"metadata": {"name": "regex", "version": "2026.9.29"}, "download_info": {"url": "https://files.pythonhosted.org/packages/ec/99/eae371ca63f7ea1f1eac025021527adb57ce3a4b67ff608b896fd31ea447/regex-2026.9.29-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}], "install_targets_hash": "2514fd384b7671a4dd05ff9008000cd2"} {"python_version": "3.10", "dependencies": [{"metadata": {"name": "litellm", "version": "1.83.14"}, "download_info": {"url": "https://files.pythonhosted.org/packages/7f/5c/1b5691575420135e90578543b2bf219497caa33cfd0af64cb38f30288450/litellm-1.83.14-py3-none-any.whl"}}, {"metadata": {"name": "httpx", "version": "0.28.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl"}}, {"metadata": {"name": "aiohttp", "version": "3.13.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/4a/4c/0051d4550fb9e8b5ca4e0fe1ccd58652340915180c5164999e6741bf2083/aiohttp-3.13.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "click", "version": "8.1.8"}, "download_info": {"url": "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl"}}, {"metadata": {"name": "fastuuid", "version": "0.14.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "importlib_metadata", "version": "8.5.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl"}}, {"metadata": {"name": "Jinja2", "version": "3.1.6"}, "download_info": {"url": "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl"}}, {"metadata": {"name": "jsonschema", "version": "4.23.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl"}}, {"metadata": {"name": "openai", "version": "2.24.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/c9/30/844dc675ee6902579b8eef01ed23917cc9319a1c9c0c14ec6e39340c96d0/openai-2.24.0-py3-none-any.whl"}}, {"metadata": {"name": "pydantic", "version": "2.12.5"}, "download_info": {"url": "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl"}}, {"metadata": {"name": "pydantic_core", "version": "2.41.5"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "python-dotenv", "version": "1.2.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl"}}, {"metadata": {"name": "tiktoken", "version": "0.12.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "tokenizers", "version": "0.22.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "anyio", "version": "4.15.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl"}}, {"metadata": {"name": "async-timeout", "version": "5.0.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl"}}, {"metadata": {"name": "distro", "version": "1.9.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl"}}, {"metadata": {"name": "httpcore", "version": "1.0.9"}, "download_info": {"url": "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl"}}, {"metadata": {"name": "huggingface_hub", "version": "1.16.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/49/79/621a7dbb80c70974f73a597275351ebe03ce5bc65cb5f8f4acb5859252bc/huggingface_hub-1.16.1-py3-none-any.whl"}}, {"metadata": {"name": "hf-xet", "version": "1.7.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a3/4c/2bf3b66c215d409655f28de1622393dde04c9461280d48c7924bb3b2decd/hf_xet-1.7.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl"}}, {"metadata": {"name": "jiter", "version": "0.17.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/93/2c/af9ca503b38683a82b6732352a861515f28e2d46fe445aea57e07fc86e06/jiter-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "multidict", "version": "6.9.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/bd/53/f65ed68b96eea2b4f6fa4f9031bdfccd8f3097c429e3d371c545d1c40ca4/multidict-6.9.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "typing_extensions", "version": "4.16.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl"}}, {"metadata": {"name": "yarl", "version": "1.25.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/28/b9/7818ac6dec7fbcd16be2d19495807c01e2c38834e57d41c63356a75e786b/yarl-1.25.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "anthropic", "version": "1.13.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/ad/9a/a835340c91bc9a233bd54c05fc438fe07cbef71ca667f24f3892a268ae20/anthropic-1.13.0-py3-none-any.whl"}}, {"metadata": {"name": "docstring_parser", "version": "0.18.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl"}}, {"metadata": {"name": "httpx2", "version": "2.13.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/d8/9c/6fe8931fd9f381042a9e4c7d5a7b4cbf7016b252bec0c99a49fce42c3326/httpx2-2.13.1-py3-none-any.whl"}}, {"metadata": {"name": "httpcore2", "version": "2.13.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/09/ba/a4568248771ce81957bfb7cc600264a40fbcda092391ee1c415c50be4bea/httpcore2-2.13.1-py3-none-any.whl"}}, {"metadata": {"name": "sniffio", "version": "1.3.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl"}}, {"metadata": {"name": "google-genai", "version": "2.30.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/46/28/faa7c02d87df1343aa8e51c816694a82eb37f41d0e83253e6e75a00b96ec/google_genai-2.30.1-py3-none-any.whl"}}, {"metadata": {"name": "google-auth", "version": "2.62.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/eb/a3/87fe9b7203ec2e56ea6a57c75e6d632eaf9487c7f7978cdad1d801bee0af/google_auth-2.62.0-py3-none-any.whl"}}, {"metadata": {"name": "requests", "version": "2.34.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl"}}, {"metadata": {"name": "charset-normalizer", "version": "3.5.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/c4/9f/9f52d2886d52645987d603425482c63c5045a3005db1354a7097e5ed1ae9/charset_normalizer-3.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "idna", "version": "3.20"}, "download_info": {"url": "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl"}}, {"metadata": {"name": "tenacity", "version": "9.1.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl"}}, {"metadata": {"name": "urllib3", "version": "2.8.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl"}}, {"metadata": {"name": "websockets", "version": "16.1.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/f3/18/a17e2f0cde02dc10154c808deed7e1d8528afff93612f70d3f0a5b19b011/websockets-16.1.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}}, {"metadata": {"name": "pytest-asyncio", "version": "1.4.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl"}}, {"metadata": {"name": "backports.asyncio.runner", "version": "1.2.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl"}}, {"metadata": {"name": "pytest", "version": "9.1.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl"}}, {"metadata": {"name": "pluggy", "version": "1.6.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl"}}, {"metadata": {"name": "filelock", "version": "4.1.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/ee/86/032133892a5de43b5a98200b01aadcad68cc255e274a762f08b8a76d2912/filelock-4.1.0-py3-none-any.whl"}}, {"metadata": {"name": "aiohappyeyeballs", "version": "2.7.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl"}}, {"metadata": {"name": "aiosignal", "version": "1.4.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl"}}, {"metadata": {"name": "annotated-types", "version": "0.8.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl"}}, {"metadata": {"name": "attrs", "version": "26.1.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl"}}, {"metadata": {"name": "certifi", "version": "2026.7.22"}, "download_info": {"url": "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl"}}, {"metadata": {"name": "cryptography", "version": "50.0.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/40/cf/6b5c8e2fd9202d98988ab7cb5cc5c991704c4ad55f492ff408e4969f83f1/cryptography-50.0.2-cp39-abi3-manylinux_2_34_x86_64.whl"}}, {"metadata": {"name": "cffi", "version": "2.1.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl"}}, {"metadata": {"name": "exceptiongroup", "version": "1.3.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl"}}, {"metadata": {"name": "frozenlist", "version": "1.8.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}}, {"metadata": {"name": "fsspec", "version": "2026.9.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl"}}, {"metadata": {"name": "h11", "version": "0.16.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl"}}, {"metadata": {"name": "iniconfig", "version": "2.3.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl"}}, {"metadata": {"name": "jsonschema-specifications", "version": "2025.9.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl"}}, {"metadata": {"name": "MarkupSafe", "version": "3.0.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/00/26/c4708ed3b0f08e8e6d7cbce3314ac130cb5352d1f62951b6fef7878f2b1e/markupsafe-3.0.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "packaging", "version": "26.3"}, "download_info": {"url": "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl"}}, {"metadata": {"name": "propcache", "version": "0.5.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/f0/51/5adee15e12a7e314cece4543fbf295b0fe1b504dc78b32dff4c8eb1e29b0/propcache-0.5.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "pyasn1_modules", "version": "0.4.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl"}}, {"metadata": {"name": "pyasn1", "version": "0.6.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/9a/3b/6163796d69c3977d1e4287bea4a6979161cbbdd170ebb430511e8e1999ce/pyasn1-0.6.4-py3-none-any.whl"}}, {"metadata": {"name": "Pygments", "version": "2.21.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl"}}, {"metadata": {"name": "PyYAML", "version": "6.0.3"}, "download_info": {"url": "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "referencing", "version": "0.37.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl"}}, {"metadata": {"name": "regex", "version": "2026.9.29"}, "download_info": {"url": "https://files.pythonhosted.org/packages/ec/99/eae371ca63f7ea1f1eac025021527adb57ce3a4b67ff608b896fd31ea447/regex-2026.9.29-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "rpds-py", "version": "0.30.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "tomli", "version": "2.5.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/60/3f/3e3f8fd0919249b0200c80fbc4f9a1e70be19f9883da71dfb7f8b9ab8aca/tomli-2.5.0-py3-none-any.whl"}}, {"metadata": {"name": "tqdm", "version": "4.70.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl"}}, {"metadata": {"name": "truststore", "version": "0.10.5"}, "download_info": {"url": "https://files.pythonhosted.org/packages/51/e9/3a7820be2bb0fe53b6bc9c3be26d3d1158004e4c3ab953aa6840b955b1e9/truststore-0.10.5-py3-none-any.whl"}}, {"metadata": {"name": "typer", "version": "0.27.3"}, "download_info": {"url": "https://files.pythonhosted.org/packages/07/ea/2e31b67051e91a133189e9c000c222502ddc6969856416de0d095de4c0b0/typer-0.27.3-py3-none-any.whl"}}, {"metadata": {"name": "annotated-doc", "version": "0.0.5"}, "download_info": {"url": "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl"}}, {"metadata": {"name": "rich", "version": "15.0.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl"}}, {"metadata": {"name": "markdown-it-py", "version": "4.2.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl"}}, {"metadata": {"name": "mdurl", "version": "0.1.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl"}}, {"metadata": {"name": "shellingham", "version": "1.5.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl"}}, {"metadata": {"name": "typing-inspection", "version": "0.4.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl"}}, {"metadata": {"name": "zipp", "version": "4.1.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/b5/98/df615823cd9419131ce19fba00de53a663794369e198aade064a244b385d/zipp-4.1.1-py3-none-any.whl"}}, {"metadata": {"name": "pycparser", "version": "3.11"}, "download_info": {"url": "https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl"}}], "install_targets_hash": "46772d82299cb934a10c3098b4647a9f"} {"python_version": "3.10", "dependencies": [{"metadata": {"name": "litellm", "version": "1.89.7"}, "download_info": {"url": "https://files.pythonhosted.org/packages/f6/1a/f087e0555d91ab5f93fb8c740447423aa785468b8edca9987724815b51ec/litellm-1.89.7-py3-none-any.whl"}}, {"metadata": {"name": "httpx", "version": "0.28.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl"}}, {"metadata": {"name": "aiohttp", "version": "3.14.5"}, "download_info": {"url": "https://files.pythonhosted.org/packages/5d/38/01f5fc732d5373d6c099db0343279cd4e6607287d336bab6ff6ae9085a0d/aiohttp-3.14.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "async-timeout", "version": "5.0.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl"}}, {"metadata": {"name": "click", "version": "8.5.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl"}}, {"metadata": {"name": "fastuuid", "version": "0.14.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/b2/72/3985be633b5a428e9eaec4287ed4b873b7c4c53a9639a8b416637223c4cd/fastuuid-0.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "httpcore", "version": "1.0.9"}, "download_info": {"url": "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl"}}, {"metadata": {"name": "importlib_metadata", "version": "8.9.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/7d/f9/97f2ca8bb3ec6e4b1d64f983ebe98b9a192faddff67fac3d6303a537e670/importlib_metadata-8.9.0-py3-none-any.whl"}}, {"metadata": {"name": "Jinja2", "version": "3.1.6"}, "download_info": {"url": "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl"}}, {"metadata": {"name": "jsonschema", "version": "4.26.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl"}}, {"metadata": {"name": "multidict", "version": "7.1.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/5a/9c/598d765e98aa02ff7204b6f11cf790e5840f5e8252d7b1d955ed0043bb94/multidict-7.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "openai", "version": "2.54.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/64/a8/bb76c7356de8ad57f59d5ff993d434df0607f07f08bcc9c9a5c275e399c0/openai-2.54.0-py3-none-any.whl"}}, {"metadata": {"name": "anyio", "version": "4.15.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl"}}, {"metadata": {"name": "distro", "version": "1.9.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl"}}, {"metadata": {"name": "jiter", "version": "0.17.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/93/2c/af9ca503b38683a82b6732352a861515f28e2d46fe445aea57e07fc86e06/jiter-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "pydantic", "version": "2.14.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl"}}, {"metadata": {"name": "pydantic_core", "version": "2.50.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/1b/1a/21a58791021397d9ca35295e3f4291c16e319155c5904334b89eb07e97e1/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "python-dotenv", "version": "1.2.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/60/d1/38f3a3405989a89ac18390803e70c6ad7c7760da4f9b83cbeca0c44a0c72/python_dotenv-1.2.4-py3-none-any.whl"}}, {"metadata": {"name": "tiktoken", "version": "0.14.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/af/c8/051e7b72a816ff50eb34a1c7c5b185cd2429ffdf59a497baea35b2b6b2dd/tiktoken-0.14.0-cp310-cp310-manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "tokenizers", "version": "0.23.3"}, "download_info": {"url": "https://files.pythonhosted.org/packages/3a/40/6706b82693715581457c6d5423eaa7faae576bb0526c5738a57085eb4449/tokenizers-0.23.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "huggingface_hub", "version": "2.2.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/eb/b0/0f7b430fd100b3a3b037fdbb314878200241082e607b3383c63d91a13a72/huggingface_hub-2.2.0-py3-none-any.whl"}}, {"metadata": {"name": "hf-xet", "version": "1.7.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a3/4c/2bf3b66c215d409655f28de1622393dde04c9461280d48c7924bb3b2decd/hf_xet-1.7.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl"}}, {"metadata": {"name": "httpx2", "version": "2.13.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/d8/9c/6fe8931fd9f381042a9e4c7d5a7b4cbf7016b252bec0c99a49fce42c3326/httpx2-2.13.1-py3-none-any.whl"}}, {"metadata": {"name": "httpcore2", "version": "2.13.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/09/ba/a4568248771ce81957bfb7cc600264a40fbcda092391ee1c415c50be4bea/httpcore2-2.13.1-py3-none-any.whl"}}, {"metadata": {"name": "typing_extensions", "version": "4.16.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl"}}, {"metadata": {"name": "yarl", "version": "1.25.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/28/b9/7818ac6dec7fbcd16be2d19495807c01e2c38834e57d41c63356a75e786b/yarl-1.25.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "anthropic", "version": "1.13.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/ad/9a/a835340c91bc9a233bd54c05fc438fe07cbef71ca667f24f3892a268ae20/anthropic-1.13.0-py3-none-any.whl"}}, {"metadata": {"name": "docstring_parser", "version": "0.18.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl"}}, {"metadata": {"name": "sniffio", "version": "1.3.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl"}}, {"metadata": {"name": "google-genai", "version": "2.30.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/46/28/faa7c02d87df1343aa8e51c816694a82eb37f41d0e83253e6e75a00b96ec/google_genai-2.30.1-py3-none-any.whl"}}, {"metadata": {"name": "google-auth", "version": "2.62.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/eb/a3/87fe9b7203ec2e56ea6a57c75e6d632eaf9487c7f7978cdad1d801bee0af/google_auth-2.62.0-py3-none-any.whl"}}, {"metadata": {"name": "requests", "version": "2.34.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl"}}, {"metadata": {"name": "charset-normalizer", "version": "3.5.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/c4/9f/9f52d2886d52645987d603425482c63c5045a3005db1354a7097e5ed1ae9/charset_normalizer-3.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "idna", "version": "3.20"}, "download_info": {"url": "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl"}}, {"metadata": {"name": "tenacity", "version": "9.1.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl"}}, {"metadata": {"name": "urllib3", "version": "2.8.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl"}}, {"metadata": {"name": "websockets", "version": "16.1.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/f3/18/a17e2f0cde02dc10154c808deed7e1d8528afff93612f70d3f0a5b19b011/websockets-16.1.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}}, {"metadata": {"name": "pytest-asyncio", "version": "1.4.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl"}}, {"metadata": {"name": "backports.asyncio.runner", "version": "1.2.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl"}}, {"metadata": {"name": "pytest", "version": "9.1.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl"}}, {"metadata": {"name": "pluggy", "version": "1.6.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl"}}, {"metadata": {"name": "filelock", "version": "4.1.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/ee/86/032133892a5de43b5a98200b01aadcad68cc255e274a762f08b8a76d2912/filelock-4.1.0-py3-none-any.whl"}}, {"metadata": {"name": "aiohappyeyeballs", "version": "2.7.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl"}}, {"metadata": {"name": "aiosignal", "version": "1.4.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl"}}, {"metadata": {"name": "annotated-types", "version": "0.8.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl"}}, {"metadata": {"name": "attrs", "version": "26.1.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl"}}, {"metadata": {"name": "certifi", "version": "2026.7.22"}, "download_info": {"url": "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl"}}, {"metadata": {"name": "cryptography", "version": "50.0.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/40/cf/6b5c8e2fd9202d98988ab7cb5cc5c991704c4ad55f492ff408e4969f83f1/cryptography-50.0.2-cp39-abi3-manylinux_2_34_x86_64.whl"}}, {"metadata": {"name": "cffi", "version": "2.1.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl"}}, {"metadata": {"name": "exceptiongroup", "version": "1.3.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl"}}, {"metadata": {"name": "frozenlist", "version": "1.8.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}}, {"metadata": {"name": "fsspec", "version": "2026.9.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl"}}, {"metadata": {"name": "h11", "version": "0.16.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl"}}, {"metadata": {"name": "iniconfig", "version": "2.3.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl"}}, {"metadata": {"name": "jsonschema-specifications", "version": "2025.9.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl"}}, {"metadata": {"name": "MarkupSafe", "version": "3.0.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/00/26/c4708ed3b0f08e8e6d7cbce3314ac130cb5352d1f62951b6fef7878f2b1e/markupsafe-3.0.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "packaging", "version": "26.3"}, "download_info": {"url": "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl"}}, {"metadata": {"name": "propcache", "version": "0.5.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/f0/51/5adee15e12a7e314cece4543fbf295b0fe1b504dc78b32dff4c8eb1e29b0/propcache-0.5.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "pyasn1_modules", "version": "0.4.2"}, "download_info": {"url": "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl"}}, {"metadata": {"name": "pyasn1", "version": "0.6.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/9a/3b/6163796d69c3977d1e4287bea4a6979161cbbdd170ebb430511e8e1999ce/pyasn1-0.6.4-py3-none-any.whl"}}, {"metadata": {"name": "Pygments", "version": "2.21.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl"}}, {"metadata": {"name": "PyYAML", "version": "6.0.3"}, "download_info": {"url": "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}, {"metadata": {"name": "referencing", "version": "0.37.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl"}}, {"metadata": {"name": "rpds-py", "version": "0.30.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}}, {"metadata": {"name": "tomli", "version": "2.5.0"}, "download_info": {"url": "https://files.pythonhosted.org/packages/60/3f/3e3f8fd0919249b0200c80fbc4f9a1e70be19f9883da71dfb7f8b9ab8aca/tomli-2.5.0-py3-none-any.whl"}}, {"metadata": {"name": "tqdm", "version": "4.70.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl"}}, {"metadata": {"name": "truststore", "version": "0.10.5"}, "download_info": {"url": "https://files.pythonhosted.org/packages/51/e9/3a7820be2bb0fe53b6bc9c3be26d3d1158004e4c3ab953aa6840b955b1e9/truststore-0.10.5-py3-none-any.whl"}}, {"metadata": {"name": "typing-inspection", "version": "0.4.4"}, "download_info": {"url": "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl"}}, {"metadata": {"name": "zipp", "version": "4.1.1"}, "download_info": {"url": "https://files.pythonhosted.org/packages/b5/98/df615823cd9419131ce19fba00de53a663794369e198aade064a244b385d/zipp-4.1.1-py3-none-any.whl"}}, {"metadata": {"name": "pycparser", "version": "3.11"}, "download_info": {"url": "https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl"}}, {"metadata": {"name": "regex", "version": "2026.9.29"}, "download_info": {"url": "https://files.pythonhosted.org/packages/ec/99/eae371ca63f7ea1f1eac025021527adb57ce3a4b67ff608b896fd31ea447/regex-2026.9.29-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}}], "install_targets_hash": "ad59f8b60919f86be9a01f748889658d"} +{"python_version": "3.10", "dependencies": [{"metadata": {"name": "rq", "version": "2.12.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/a4/c2/995863e88669133a058c2a6a912b62d18a64fa7baaf78eb66aaa4350b48d/rq-2.12.0-py3-none-any.whl"}}, {"metadata": {"name": "fakeredis", "version": "2.35.1"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/6f/27/b8b057a23f7777177e92d3a602fd866751b6b45014964548997e92e048fd/fakeredis-2.35.1-py3-none-any.whl"}}, {"metadata": {"name": "rq-scheduler", "version": "0.14.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/bb/d0/28cedca9f3b321f30e69d644c2dcd7097ec21570ec9606fde56750621300/rq_scheduler-0.14.0-py2.py3-none-any.whl"}}, {"metadata": {"name": "click", "version": "8.5.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl"}}, {"metadata": {"name": "crontab", "version": "1.0.5"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/d6/36/a255b6f5a2e22df03fd2b2f3088974b44b8c9e9407e26b44742cb7cfbf5b/crontab-1.0.5.tar.gz"}}, {"metadata": {"name": "redis", "version": "8.1.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/66/9d/c5731f6e3608663d4d3656fd8d3aecee8b509c3082818f5a13eae925baea/redis-8.1.0-py3-none-any.whl"}}, {"metadata": {"name": "async-timeout", "version": "5.0.1"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl"}}, {"metadata": {"name": "sortedcontainers", "version": "2.4.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl"}}, {"metadata": {"name": "typing_extensions", "version": "4.16.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl"}}, {"metadata": {"name": "croniter", "version": "6.2.4"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/cd/ba/d678e5bd329646ca51d3c92addbc77804e86d21f4b6b6a027218e6abb010/croniter-6.2.4-py3-none-any.whl"}}, {"metadata": {"name": "freezegun", "version": "1.5.5"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/5e/2e/b41d8a1a917d6581fc27a35d05561037b048e47df50f27f8ac9c7e27a710/freezegun-1.5.5-py3-none-any.whl"}}, {"metadata": {"name": "python-dateutil", "version": "2.9.0.post0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl"}}, {"metadata": {"name": "six", "version": "1.17.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl"}}], "install_targets_hash": "6ecfac04579ee4f4dcd297f14e661a0c"} +{"python_version": "3.13", "dependencies": [{"metadata": {"name": "rq", "version": "2.12.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/a4/c2/995863e88669133a058c2a6a912b62d18a64fa7baaf78eb66aaa4350b48d/rq-2.12.0-py3-none-any.whl"}}, {"metadata": {"name": "fakeredis", "version": "2.35.1"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/6f/27/b8b057a23f7777177e92d3a602fd866751b6b45014964548997e92e048fd/fakeredis-2.35.1-py3-none-any.whl"}}, {"metadata": {"name": "rq-scheduler", "version": "0.14.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/bb/d0/28cedca9f3b321f30e69d644c2dcd7097ec21570ec9606fde56750621300/rq_scheduler-0.14.0-py2.py3-none-any.whl"}}, {"metadata": {"name": "click", "version": "8.5.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl"}}, {"metadata": {"name": "crontab", "version": "1.0.5"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/d6/36/a255b6f5a2e22df03fd2b2f3088974b44b8c9e9407e26b44742cb7cfbf5b/crontab-1.0.5.tar.gz"}}, {"metadata": {"name": "redis", "version": "8.1.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/66/9d/c5731f6e3608663d4d3656fd8d3aecee8b509c3082818f5a13eae925baea/redis-8.1.0-py3-none-any.whl"}}, {"metadata": {"name": "sortedcontainers", "version": "2.4.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl"}}, {"metadata": {"name": "croniter", "version": "6.2.4"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/cd/ba/d678e5bd329646ca51d3c92addbc77804e86d21f4b6b6a027218e6abb010/croniter-6.2.4-py3-none-any.whl"}}, {"metadata": {"name": "freezegun", "version": "1.5.5"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/5e/2e/b41d8a1a917d6581fc27a35d05561037b048e47df50f27f8ac9c7e27a710/freezegun-1.5.5-py3-none-any.whl"}}, {"metadata": {"name": "python-dateutil", "version": "2.9.0.post0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl"}}, {"metadata": {"name": "six", "version": "1.17.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl"}}], "install_targets_hash": "6ecfac04579ee4f4dcd297f14e661a0c"} +{"python_version": "3.14", "dependencies": [{"metadata": {"name": "rq", "version": "2.12.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/a4/c2/995863e88669133a058c2a6a912b62d18a64fa7baaf78eb66aaa4350b48d/rq-2.12.0-py3-none-any.whl"}}, {"metadata": {"name": "fakeredis", "version": "2.35.1"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/6f/27/b8b057a23f7777177e92d3a602fd866751b6b45014964548997e92e048fd/fakeredis-2.35.1-py3-none-any.whl"}}, {"metadata": {"name": "rq-scheduler", "version": "0.14.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/bb/d0/28cedca9f3b321f30e69d644c2dcd7097ec21570ec9606fde56750621300/rq_scheduler-0.14.0-py2.py3-none-any.whl"}}, {"metadata": {"name": "click", "version": "8.5.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl"}}, {"metadata": {"name": "crontab", "version": "1.0.5"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/d6/36/a255b6f5a2e22df03fd2b2f3088974b44b8c9e9407e26b44742cb7cfbf5b/crontab-1.0.5.tar.gz"}}, {"metadata": {"name": "redis", "version": "8.1.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/66/9d/c5731f6e3608663d4d3656fd8d3aecee8b509c3082818f5a13eae925baea/redis-8.1.0-py3-none-any.whl"}}, {"metadata": {"name": "sortedcontainers", "version": "2.4.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl"}}, {"metadata": {"name": "croniter", "version": "6.2.4"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/cd/ba/d678e5bd329646ca51d3c92addbc77804e86d21f4b6b6a027218e6abb010/croniter-6.2.4-py3-none-any.whl"}}, {"metadata": {"name": "freezegun", "version": "1.5.5"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/5e/2e/b41d8a1a917d6581fc27a35d05561037b048e47df50f27f8ac9c7e27a710/freezegun-1.5.5-py3-none-any.whl"}}, {"metadata": {"name": "python-dateutil", "version": "2.9.0.post0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl"}}, {"metadata": {"name": "six", "version": "1.17.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl"}}], "install_targets_hash": "6ecfac04579ee4f4dcd297f14e661a0c"} +{"python_version": "3.14t", "dependencies": [{"metadata": {"name": "rq", "version": "2.12.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/a4/c2/995863e88669133a058c2a6a912b62d18a64fa7baaf78eb66aaa4350b48d/rq-2.12.0-py3-none-any.whl"}}, {"metadata": {"name": "fakeredis", "version": "2.35.1"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/6f/27/b8b057a23f7777177e92d3a602fd866751b6b45014964548997e92e048fd/fakeredis-2.35.1-py3-none-any.whl"}}, {"metadata": {"name": "rq-scheduler", "version": "0.14.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/bb/d0/28cedca9f3b321f30e69d644c2dcd7097ec21570ec9606fde56750621300/rq_scheduler-0.14.0-py2.py3-none-any.whl"}}, {"metadata": {"name": "click", "version": "8.5.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl"}}, {"metadata": {"name": "crontab", "version": "1.0.5"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/d6/36/a255b6f5a2e22df03fd2b2f3088974b44b8c9e9407e26b44742cb7cfbf5b/crontab-1.0.5.tar.gz"}}, {"metadata": {"name": "redis", "version": "8.1.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/66/9d/c5731f6e3608663d4d3656fd8d3aecee8b509c3082818f5a13eae925baea/redis-8.1.0-py3-none-any.whl"}}, {"metadata": {"name": "sortedcontainers", "version": "2.4.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl"}}, {"metadata": {"name": "croniter", "version": "6.2.4"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/cd/ba/d678e5bd329646ca51d3c92addbc77804e86d21f4b6b6a027218e6abb010/croniter-6.2.4-py3-none-any.whl"}}, {"metadata": {"name": "freezegun", "version": "1.5.5"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/5e/2e/b41d8a1a917d6581fc27a35d05561037b048e47df50f27f8ac9c7e27a710/freezegun-1.5.5-py3-none-any.whl"}}, {"metadata": {"name": "python-dateutil", "version": "2.9.0.post0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl"}}, {"metadata": {"name": "six", "version": "1.17.0"}, "download_info": {"url": "https://sfw.security.sentry.io/pypi/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl"}}], "install_targets_hash": "6ecfac04579ee4f4dcd297f14e661a0c"} diff --git a/sentry_sdk/integrations/rq.py b/sentry_sdk/integrations/rq.py index d6b51ba77d..252c327378 100644 --- a/sentry_sdk/integrations/rq.py +++ b/sentry_sdk/integrations/rq.py @@ -1,9 +1,12 @@ import functools +import re +import time import weakref import sentry_sdk from sentry_sdk.api import continue_trace from sentry_sdk.consts import OP, SPANDATA +from sentry_sdk.crons import MonitorStatus, capture_checkin from sentry_sdk.integrations import DidNotEnable, Integration, _check_minimum_version from sentry_sdk.integrations.logging import ignore_logger from sentry_sdk.scope import Scope, should_send_default_pii @@ -16,6 +19,9 @@ event_from_exception, format_timestamp, has_data_collection_enabled, + logger, + match_regex_list, + now, parse_version, ) @@ -39,11 +45,16 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Any, Callable + from typing import Any, Callable, List, Optional, Set, Tuple from rq.job import Job - from sentry_sdk._types import Event, EventProcessor + from sentry_sdk._types import ( + Event, + EventProcessor, + MonitorConfig, + MonitorConfigScheduleUnit, + ) from sentry_sdk.utils import ExcInfo @@ -51,6 +62,14 @@ class RqIntegration(Integration): identifier = "rq" origin = f"auto.queue.{identifier}" + def __init__( + self, + monitor_cron_jobs: bool = False, + exclude_cron_jobs: "Optional[List[str]]" = None, + ) -> None: + self.monitor_cron_jobs = monitor_cron_jobs + self.exclude_cron_jobs = exclude_cron_jobs + @staticmethod def setup_once() -> None: version = parse_version(RQ_VERSION) @@ -69,9 +88,14 @@ def sentry_patched_perform_job( self: "Any", job: "Job", queue: "Queue", *args: "Any", **kwargs: "Any" ) -> bool: client = sentry_sdk.get_client() - if client.get_integration(RqIntegration) is None: + integration = client.get_integration(RqIntegration) + if integration is None: return old_perform_job(self, job, queue, *args, **kwargs) + check_in = None + with capture_internal_exceptions(): + check_in = _start_check_in(integration, job) + with sentry_sdk.new_scope() as scope: scope.clear_breadcrumbs() scope.add_event_processor(_make_event_processor(weakref.ref(job))) @@ -122,6 +146,10 @@ def sentry_patched_perform_job( rv = old_perform_job(self, job, queue, *args, **kwargs) + if check_in is not None: + with capture_internal_exceptions(): + _finish_check_in(check_in, rv) + if self.is_horse: # We're inside of a forked process and RQ is # about to call `os._exit`. Make sure that our @@ -229,3 +257,113 @@ def _capture_exception(exc_info: "ExcInfo", **kwargs: "Any") -> None: ) sentry_sdk.capture_event(event, hint=hint) + + +# rq generates `str(uuid4())` job ids. +_GENERATED_JOB_ID = re.compile( + r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" +) +_warned_func_names: "Set[str]" = set() +_CRONTAB_FIELD = re.compile(r"[0-9*,/-]+") +_INTERVAL_UNITS: "Tuple[Tuple[MonitorConfigScheduleUnit, int], ...]" = ( + ("day", 60 * 60 * 24), + ("hour", 60 * 60), + ("minute", 60), +) + + +def _get_crontab_config(cron_string: str) -> "Optional[MonitorConfig]": + fields = cron_string.split() + if len(fields) != 5 or not all(_CRONTAB_FIELD.fullmatch(f) for f in fields): + return None + # Day of month and day of week are matched differently by different + # cron parsers when both are set. + if fields[2] != "*" and fields[4] != "*": + return None + # rq-scheduler evaluates crontabs in the system's local time. + if time.timezone != 0 or (time.daylight and time.altzone != 0): + return None + return { + "schedule": {"type": "crontab", "value": " ".join(fields)}, + "timezone": "UTC", + } + + +def _get_monitor_config(job: "Job") -> "Optional[MonitorConfig]": + # Jobs with `repeat` stop after a number of runs. + if job.meta.get("repeat") is not None: + return None + + cron_string = job.meta.get("cron_string") + if isinstance(cron_string, str): + return _get_crontab_config(cron_string) + + interval = job.meta.get("interval") + if isinstance(interval, int): + for unit, seconds in _INTERVAL_UNITS: + if interval >= seconds and interval % seconds == 0: + return { + "schedule": { + "type": "interval", + "value": interval // seconds, + "unit": unit, + } + } + return None + + +def _start_check_in( + integration: "RqIntegration", job: "Job" +) -> "Optional[Tuple[str, Optional[MonitorConfig], Optional[str], float]]": + """ + Send an `in_progress` check-in for jobs that rq-scheduler's `cron()` and + `schedule(interval=...)` re-enqueue. It stores their schedule in the meta. + """ + if not integration.monitor_cron_jobs: + return None + if "cron_string" not in job.meta and "interval" not in job.meta: + return None + + # Without an explicit `id`, every scheduled job gets a random one. + if _GENERATED_JOB_ID.fullmatch(job.id): + monitor_slug = job.func_name + if monitor_slug not in _warned_func_names: + _warned_func_names.add(monitor_slug) + logger.warning( + "RQ scheduled job %r has no explicit `id`, so its function name " + "is used as its monitor slug, and jobs for the same function " + "share a monitor. Pass `id` to give it its own monitor.", + monitor_slug, + ) + else: + monitor_slug = job.id + if match_regex_list(monitor_slug, integration.exclude_cron_jobs): + return None + + monitor_config = _get_monitor_config(job) + if monitor_config is None: + logger.debug( + "Schedule of RQ job %r can't be expressed as a Sentry Crons " + "schedule. Sending check-ins without a monitor config.", + monitor_slug, + ) + + check_in_id = capture_checkin( + monitor_slug=monitor_slug, + monitor_config=monitor_config, + status=MonitorStatus.IN_PROGRESS, + ) + return monitor_slug, monitor_config, check_in_id, now() + + +def _finish_check_in( + check_in: "Tuple[str, Optional[MonitorConfig], Optional[str], float]", ok: bool +) -> None: + monitor_slug, monitor_config, check_in_id, start = check_in + capture_checkin( + monitor_slug=monitor_slug, + monitor_config=monitor_config, + check_in_id=check_in_id, + duration=now() - start, + status=MonitorStatus.OK if ok else MonitorStatus.ERROR, + ) diff --git a/tests/integrations/rq/test_rq_crons.py b/tests/integrations/rq/test_rq_crons.py new file mode 100644 index 0000000000..0183050977 --- /dev/null +++ b/tests/integrations/rq/test_rq_crons.py @@ -0,0 +1,210 @@ +import time +from datetime import datetime, timedelta, timezone + +import pytest +import rq +from fakeredis import FakeStrictRedis + +from sentry_sdk.integrations.rq import RqIntegration, _get_crontab_config + +rq_scheduler = pytest.importorskip("rq_scheduler") + + +def _set_local_timezone(monkeypatch, tz): + monkeypatch.setenv("TZ", tz) + time.tzset() + + +@pytest.fixture(autouse=True) +def utc_local_time(monkeypatch): + _set_local_timezone(monkeypatch, "UTC") + yield + monkeypatch.undo() + time.tzset() + + +def ok_job(): + pass + + +def failing_job(): + raise ValueError("oops") + + +def _check_ins(envelopes): + return [ + item.payload.json + for envelope in envelopes + for item in envelope.items + if item.headers.get("type") == "check_in" + ] + + +def _run(schedule): + """Schedule jobs with rq-scheduler, push them to the queue and run them.""" + connection = FakeStrictRedis() + queue = rq.Queue(connection=connection) + scheduler = rq_scheduler.Scheduler(queue=queue, connection=connection) + schedule(scheduler) + + for job in scheduler.get_jobs(): + scheduler.enqueue_job(job) + + rq.SimpleWorker([queue], connection=connection).work(burst=True) + + +def test_cron_job_ok(sentry_init, capture_envelopes): + sentry_init(integrations=[RqIntegration(monitor_cron_jobs=True)]) + envelopes = capture_envelopes() + + _run(lambda s: s.cron("*/5 * * * *", func=ok_job, id="cleanup")) + + in_progress, ok = _check_ins(envelopes) + assert in_progress["monitor_slug"] == "cleanup" + assert in_progress["status"] == "in_progress" + assert in_progress["monitor_config"] == { + "schedule": {"type": "crontab", "value": "*/5 * * * *"}, + "timezone": "UTC", + } + assert ok["monitor_slug"] == "cleanup" + assert ok["status"] == "ok" + assert ok["check_in_id"] == in_progress["check_in_id"] + assert ok["duration"] >= 0 + + +def test_cron_job_error(sentry_init, capture_envelopes): + sentry_init(integrations=[RqIntegration(monitor_cron_jobs=True)]) + envelopes = capture_envelopes() + + _run(lambda s: s.cron("0 3 * * *", func=failing_job, id="nightly")) + + check_ins = _check_ins(envelopes) + assert [c["status"] for c in check_ins] == ["in_progress", "error"] + + events = [envelope.get_event() for envelope in envelopes] + (error,) = [e for e in events if e is not None and e.get("exception")] + assert error["exception"]["values"][0]["type"] == "ValueError" + + +def test_cron_job_generated_id(sentry_init, capture_envelopes): + sentry_init(integrations=[RqIntegration(monitor_cron_jobs=True)]) + envelopes = capture_envelopes() + + _run(lambda s: s.cron("0 * * * *", func=ok_job)) + + assert {c["monitor_slug"] for c in _check_ins(envelopes)} == { + "tests.integrations.rq.test_rq_crons.ok_job" + } + + +@pytest.mark.parametrize( + "interval, schedule", + [ + (300, {"type": "interval", "value": 5, "unit": "minute"}), + (7200, {"type": "interval", "value": 2, "unit": "hour"}), + (86400, {"type": "interval", "value": 1, "unit": "day"}), + (30, None), + (90, None), + ], +) +def test_interval_job(sentry_init, capture_envelopes, interval, schedule): + sentry_init(integrations=[RqIntegration(monitor_cron_jobs=True)]) + envelopes = capture_envelopes() + + _run( + lambda s: s.schedule( + datetime.now(timezone.utc), ok_job, interval=interval, id="poll" + ) + ) + + check_ins = _check_ins(envelopes) + assert [c["status"] for c in check_ins] == ["in_progress", "ok"] + assert {c["monitor_slug"] for c in check_ins} == {"poll"} + for check_in in check_ins: + assert check_in.get("monitor_config") == ( + None if schedule is None else {"schedule": schedule} + ) + + +def test_repeat_job_without_config(sentry_init, capture_envelopes): + sentry_init(integrations=[RqIntegration(monitor_cron_jobs=True)]) + envelopes = capture_envelopes() + + _run(lambda s: s.cron("0 * * * *", func=ok_job, id="limited", repeat=3)) + + check_ins = _check_ins(envelopes) + assert [c["status"] for c in check_ins] == ["in_progress", "ok"] + assert all("monitor_config" not in c for c in check_ins) + + +def test_one_off_jobs_not_monitored(sentry_init, capture_envelopes): + sentry_init(integrations=[RqIntegration(monitor_cron_jobs=True)]) + envelopes = capture_envelopes() + + def schedule(scheduler): + scheduler.enqueue_at(datetime.now(timezone.utc), ok_job) + scheduler.enqueue_in(timedelta(seconds=0), ok_job) + scheduler.schedule(datetime.now(timezone.utc), ok_job) + + _run(schedule) + + assert _check_ins(envelopes) == [] + + +def test_not_monitored_by_default(sentry_init, capture_envelopes): + sentry_init(integrations=[RqIntegration()]) + envelopes = capture_envelopes() + + _run(lambda s: s.cron("0 * * * *", func=ok_job, id="cleanup")) + + assert _check_ins(envelopes) == [] + + +def test_exclude_cron_jobs(sentry_init, capture_envelopes): + sentry_init( + integrations=[ + RqIntegration(monitor_cron_jobs=True, exclude_cron_jobs=["clean.*"]) + ] + ) + envelopes = capture_envelopes() + + def schedule(scheduler): + scheduler.cron("0 * * * *", func=ok_job, id="cleanup") + scheduler.cron("0 * * * *", func=ok_job, id="report") + + _run(schedule) + + assert {c["monitor_slug"] for c in _check_ins(envelopes)} == {"report"} + + +@pytest.mark.parametrize( + "cron_string", + ["* * * * *", "*/5 * * * *", "0 9-17 * * 1-5", "30 2 1,15 * *", "0 0 * 1/3 *"], +) +def test_get_crontab_config(cron_string): + assert _get_crontab_config(cron_string) == { + "schedule": {"type": "crontab", "value": cron_string}, + "timezone": "UTC", + } + + +@pytest.mark.parametrize( + "cron_string", + [ + # seconds + "*/10 * * * * *", + "@daily", + "0 0 L * *", + "0 0 * * MON", + # day of month and day of week + "0 0 1 * 1", + ], +) +def test_get_crontab_config_not_representable(cron_string): + assert _get_crontab_config(cron_string) is None + + +@pytest.mark.parametrize("tz", ["America/New_York", "Europe/London"]) +def test_get_crontab_config_local_time(monkeypatch, tz): + _set_local_timezone(monkeypatch, tz) + assert _get_crontab_config("0 0 * * *") is None diff --git a/tox.ini b/tox.ini index 15facb2c4d..1ea46c32f8 100644 --- a/tox.ini +++ b/tox.ini @@ -15634,85 +15634,112 @@ deps = rq-v2.12.0: rq==2.12.0 py3.10-rq-v2.12.0: fakeredis==2.35.1 + py3.10-rq-v2.12.0: rq-scheduler==0.14.0 py3.10-rq-v2.12.0: click==8.5.0 + py3.10-rq-v2.12.0: crontab==1.0.5 py3.10-rq-v2.12.0: redis==8.1.0 py3.10-rq-v2.12.0: async-timeout==5.0.1 py3.10-rq-v2.12.0: sortedcontainers==2.4.0 py3.10-rq-v2.12.0: typing_extensions==4.16.0 py3.10-rq-v2.12.0: croniter==6.2.4 + py3.10-rq-v2.12.0: freezegun==1.5.5 py3.10-rq-v2.12.0: python-dateutil==2.9.0.post0 py3.10-rq-v2.12.0: six==1.17.0 py3.13-rq-v2.12.0: fakeredis==2.35.1 + py3.13-rq-v2.12.0: rq-scheduler==0.14.0 py3.13-rq-v2.12.0: click==8.5.0 + py3.13-rq-v2.12.0: crontab==1.0.5 py3.13-rq-v2.12.0: redis==8.1.0 py3.13-rq-v2.12.0: sortedcontainers==2.4.0 py3.13-rq-v2.12.0: croniter==6.2.4 + py3.13-rq-v2.12.0: freezegun==1.5.5 py3.13-rq-v2.12.0: python-dateutil==2.9.0.post0 py3.13-rq-v2.12.0: six==1.17.0 py3.14-rq-v2.12.0: fakeredis==2.35.1 + py3.14-rq-v2.12.0: rq-scheduler==0.14.0 py3.14-rq-v2.12.0: click==8.5.0 + py3.14-rq-v2.12.0: crontab==1.0.5 py3.14-rq-v2.12.0: redis==8.1.0 py3.14-rq-v2.12.0: sortedcontainers==2.4.0 py3.14-rq-v2.12.0: croniter==6.2.4 + py3.14-rq-v2.12.0: freezegun==1.5.5 py3.14-rq-v2.12.0: python-dateutil==2.9.0.post0 py3.14-rq-v2.12.0: six==1.17.0 py3.14t-rq-v2.12.0: fakeredis==2.35.1 + py3.14t-rq-v2.12.0: rq-scheduler==0.14.0 py3.14t-rq-v2.12.0: click==8.5.0 + py3.14t-rq-v2.12.0: crontab==1.0.5 py3.14t-rq-v2.12.0: redis==8.1.0 py3.14t-rq-v2.12.0: sortedcontainers==2.4.0 py3.14t-rq-v2.12.0: croniter==6.2.4 + py3.14t-rq-v2.12.0: freezegun==1.5.5 py3.14t-rq-v2.12.0: python-dateutil==2.9.0.post0 py3.14t-rq-v2.12.0: six==1.17.0 rq-latest: rq==2.12.0 rq-latest: fakeredis<2.36.0 + rq-latest: rq-scheduler py3.10-rq-latest: fakeredis==2.35.1 + py3.10-rq-latest: rq-scheduler==0.14.0 py3.10-rq-latest: click==8.5.0 + py3.10-rq-latest: crontab==1.0.5 py3.10-rq-latest: redis==8.1.0 py3.10-rq-latest: async-timeout==5.0.1 py3.10-rq-latest: sortedcontainers==2.4.0 py3.10-rq-latest: typing_extensions==4.16.0 py3.10-rq-latest: croniter==6.2.4 + py3.10-rq-latest: freezegun==1.5.5 py3.10-rq-latest: python-dateutil==2.9.0.post0 py3.10-rq-latest: six==1.17.0 py3.13-rq-latest: fakeredis==2.35.1 + py3.13-rq-latest: rq-scheduler==0.14.0 py3.13-rq-latest: click==8.5.0 + py3.13-rq-latest: crontab==1.0.5 py3.13-rq-latest: redis==8.1.0 py3.13-rq-latest: sortedcontainers==2.4.0 py3.13-rq-latest: croniter==6.2.4 + py3.13-rq-latest: freezegun==1.5.5 py3.13-rq-latest: python-dateutil==2.9.0.post0 py3.13-rq-latest: six==1.17.0 py3.14-rq-latest: fakeredis==2.35.1 + py3.14-rq-latest: rq-scheduler==0.14.0 py3.14-rq-latest: click==8.5.0 + py3.14-rq-latest: crontab==1.0.5 py3.14-rq-latest: redis==8.1.0 py3.14-rq-latest: sortedcontainers==2.4.0 py3.14-rq-latest: croniter==6.2.4 + py3.14-rq-latest: freezegun==1.5.5 py3.14-rq-latest: python-dateutil==2.9.0.post0 py3.14-rq-latest: six==1.17.0 py3.14t-rq-latest: fakeredis==2.35.1 + py3.14t-rq-latest: rq-scheduler==0.14.0 py3.14t-rq-latest: click==8.5.0 + py3.14t-rq-latest: crontab==1.0.5 py3.14t-rq-latest: redis==8.1.0 py3.14t-rq-latest: sortedcontainers==2.4.0 py3.14t-rq-latest: croniter==6.2.4 + py3.14t-rq-latest: freezegun==1.5.5 py3.14t-rq-latest: python-dateutil==2.9.0.post0 py3.14t-rq-latest: six==1.17.0 rq: fakeredis rq-v1.16.2: fakeredis<2.36.0 rq-v2.12.0: fakeredis<2.36.0 + rq-v2.12.0: rq-scheduler rq-v0.6.0: fakeredis<1.0 rq-v0.6.0: redis<3.2.2 rq-v0.13.0: fakeredis>=1.0,<1.7.4 {py3.6,py3.7}-rq: fakeredis!=2.26.0 rq-latest: fakeredis<2.36.0 + rq-latest: rq-scheduler spark-v3.0.3: pyspark==3.0.3