From 5146154e9099ed4c0ba6a8af929e905c8609ce29 Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 19 Feb 2026 08:53:40 +0100 Subject: [PATCH 01/22] Add gevent to requirements (#6369) * Bump pip and setuptools and switch from setup.py to pip install * Add gevent to requirements, bumps for compatibility and regenerate requirement files. * Display which components fail unit tests. * Add checks for eventlet code in monkey patch --- Makefile | 134 ++++++++--------------- fixed-requirements.txt | 7 +- requirements.txt | 5 +- st2actions/in-requirements.txt | 1 + st2actions/requirements.txt | 3 +- st2api/in-requirements.txt | 1 + st2api/requirements.txt | 3 +- st2auth/in-requirements.txt | 1 + st2auth/requirements.txt | 3 +- st2common/in-requirements.txt | 1 + st2common/requirements.txt | 5 +- st2common/st2common/util/concurrency.py | 6 +- st2common/st2common/util/monkey_patch.py | 70 ++++++------ st2reactor/in-requirements.txt | 1 + st2reactor/requirements.txt | 3 +- st2stream/in-requirements.txt | 1 + st2stream/requirements.txt | 3 +- 17 files changed, 114 insertions(+), 134 deletions(-) diff --git a/Makefile b/Makefile index 84679bceda..f587b89667 100644 --- a/Makefile +++ b/Makefile @@ -59,8 +59,8 @@ ST2TESTS_REDIS_PORT := 6379 # Pin common pip version here across all the targets # Note! Periodic maintenance pip upgrades are required to be up-to-date with the latest pip security fixes and updates -PIP_VERSION ?= 25.0.1 -SETUPTOOLS_VERSION ?= 75.3.2 +PIP_VERSION ?= 26.0.1 +SETUPTOOLS_VERSION ?= 80.6.0 PIP_OPTIONS := $(ST2_PIP_OPTIONS) ifndef PYLINT_CONCURRENCY @@ -174,13 +174,8 @@ install-runners: @echo "================== INSTALL RUNNERS ====================" @echo "" # NOTE: We use xargs to speed things up by installing runners in parallel - echo -e "$(COMPONENTS_RUNNERS)" | tr -d "\n" | xargs -P $(XARGS_CONCURRENCY) -d " " -n1 -i sh -c ". $(VIRTUALENV_DIR)/bin/activate; cd $$(pwd)/{} ; python setup.py develop --no-deps" - #@for component in $(COMPONENTS_RUNNERS); do \ - # echo "==========================================================="; \ - # echo "Installing runner:" $$component; \ - # echo "==========================================================="; \ - # #(. $(VIRTUALENV_DIR)/bin/activate; cd $$component; python setup.py develop --no-deps); \ - #done + echo -e "$(COMPONENTS_RUNNERS)" | tr -d "\n" | xargs -P $(XARGS_CONCURRENCY) -d " " -n1 -i sh -c ". $(VIRTUALENV_DIR)/bin/activate; cd $$(pwd)/{} ; python -m pip install --editable . --no-deps" + .PHONY: install-mock-runners install-mock-runners: @@ -188,13 +183,8 @@ install-mock-runners: @echo "================== INSTALL MOCK RUNNERS ====================" @echo "" # NOTE: We use xargs to speed things up by installing runners in parallel - echo -e "$(MOCK_RUNNERS)" | tr -d "\n" | xargs -P $(XARGS_CONCURRENCY) -d " " -n1 -i sh -c ". $(VIRTUALENV_DIR)/bin/activate; cd $$(pwd)/{} ; python setup.py develop --no-deps" - #@for component in $(MOCK_RUNNERS); do \ - # echo "==========================================================="; \ - # echo "Installing mock runner:" $$component; \ - # echo "==========================================================="; \ - # (. $(VIRTUALENV_DIR)/bin/activate; cd $$component; python setup.py develop --no-deps); \ - #done + echo -e "$(MOCK_RUNNERS)" | tr -d "\n" | xargs -P $(XARGS_CONCURRENCY) -d " " -n1 -i sh -c ". $(VIRTUALENV_DIR)/bin/activate; cd $$(pwd)/{} ; python -m pip install --editable . --no-deps" + .PHONY: check-requirements .check-requirements: @@ -257,7 +247,7 @@ check-python-packages: echo "==========================================================="; \ echo "Checking component:" $$component; \ echo "==========================================================="; \ - (set -e; cd $$component; ../$(VIRTUALENV_COMPONENTS_DIR)/bin/python setup.py --version) || exit 1; \ + (set -e; cd $$component; ../$(VIRTUALENV_COMPONENTS_DIR)/bin/python -m setuptools_scm) || exit 1; \ done .PHONY: check-python-packages-nightly @@ -274,9 +264,9 @@ check-python-packages-nightly: echo "==========================================================="; \ echo "Checking component:" $$component; \ echo "==========================================================="; \ - (set -e; cd $$component; ../$(VIRTUALENV_COMPONENTS_DIR)/bin/python setup.py --version) || exit 1; \ - (set -e; cd $$component; ../$(VIRTUALENV_COMPONENTS_DIR)/bin/python setup.py sdist bdist_wheel) || exit 1; \ - (set -e; cd $$component; ../$(VIRTUALENV_COMPONENTS_DIR)/bin/python setup.py develop --no-deps) || exit 1; \ + (set -e; cd $$component; ../$(VIRTUALENV_COMPONENTS_DIR)/bin/python -m setuptools_scm) || exit 1; \ + (set -e; cd $$component; ../$(VIRTUALENV_COMPONENTS_DIR)/bin/python -m build) || exit 1; \ + (set -e; cd $$component; ../$(VIRTUALENV_COMPONENTS_DIR)/bin/python -m pip install --editable . --no-deps) || exit 1; \ ($(VIRTUALENV_COMPONENTS_DIR)/bin/python -c "import $$component") || exit 1; \ (set -e; cd $$component; rm -rf dist/; rm -rf $$component.egg-info) || exit 1; \ done @@ -652,13 +642,6 @@ distclean: clean scripts/write-headers.sh $$component/dist_utils.py || break;\ done - # Copy over CHANGELOG.RST, CONTRIBUTING.RST and LICENSE file to each component directory - #@for component in $(COMPONENTS_TEST); do\ - # test -s $$component/README.rst || cp -f README.rst $$component/; \ - # cp -f CONTRIBUTING.rst $$component/; \ - # cp -f LICENSE $$component/; \ - #done - .PHONY: .requirements .requirements: virtualenv $(VIRTUALENV_DIR)/bin/pip install --upgrade "pip==$(PIP_VERSION)" @@ -670,16 +653,10 @@ distclean: clean # Remove any *.egg-info files which polute PYTHONPATH rm -rf *.egg-info* - # Generate finall requirements.txt file for each component + # Generate final requirements.txt file for each component # NOTE: We use xargs to speed things up by running commands in parallel echo -e "$(COMPONENTS_WITH_RUNNERS)" | tr -d "\n" | xargs -P $(XARGS_CONCURRENCY) -d " " -n1 -i sh -c "$(VIRTUALENV_DIR)/bin/python scripts/fixate-requirements.py --skip=virtualenv,virtualenv-osx -s {}/in-requirements.txt -f fixed-requirements.txt -o {}/requirements.txt" - #@for component in $(COMPONENTS_WITH_RUNNERS); do\ - # echo "==========================================================="; \ - # echo "Generating requirements.txt for" $$component; \ - # $(VIRTUALENV_DIR)/bin/python scripts/fixate-requirements.py --skip=virtualenv,virtualenv-osx -s $$component/in-requirements.txt -f fixed-requirements.txt -o $$component/requirements.txt; \ - #done - @echo "===========================================================" .PHONY: requirements @@ -707,19 +684,19 @@ requirements: virtualenv .requirements .sdist-requirements install-runners insta # NOTE: We pass --no-deps to the script so we don't install all the # package dependencies which are already installed as part of "requirements" # make targets. This speeds up the build - (cd ${ROOT_DIR}/st2common; ${ROOT_DIR}/$(VIRTUALENV_DIR)/bin/python setup.py develop --no-deps) + (cd ${ROOT_DIR}/st2common; ${ROOT_DIR}/$(VIRTUALENV_DIR)/bin/python -m pip install --editable . --no-deps) # Install st2common to register metrics drivers # NOTE: We pass --no-deps to the script so we don't install all the # package dependencies which are already installed as part of "requirements" # make targets. This speeds up the build - (cd ${ROOT_DIR}/st2common; ${ROOT_DIR}/$(VIRTUALENV_DIR)/bin/python setup.py develop --no-deps) + (cd ${ROOT_DIR}/st2common; ${ROOT_DIR}/$(VIRTUALENV_DIR)/bin/python -m pip install --editable . --no-deps) # Install st2auth to register SSO drivers # NOTE: We pass --no-deps to the script so we don't install all the # package dependencies which are already installed as part of "requirements" # make targets. This speeds up the build - (cd ${ROOT_DIR}/st2auth; ${ROOT_DIR}/$(VIRTUALENV_DIR)/bin/python setup.py develop --no-deps) + (cd ${ROOT_DIR}/st2auth; ${ROOT_DIR}/$(VIRTUALENV_DIR)/bin/python -m pip install --editable . --no-deps) # Some of the tests rely on submodule so we need to make sure submodules are check out git submodule update --init --recursive --remote @@ -768,21 +745,6 @@ endif echo 'export PYTHONPATH' >> $(VIRTUALENV_DIR)/bin/activate touch $(VIRTUALENV_DIR)/bin/activate - # Setup PYTHONPATH in fish activate script... - #echo '' >> $(VIRTUALENV_DIR)/bin/activate.fish - #echo 'set -gx _OLD_PYTHONPATH $$PYTHONPATH' >> $(VIRTUALENV_DIR)/bin/activate.fish - #echo 'set -gx PYTHONPATH $$_OLD_PYTHONPATH $(COMPONENT_PYTHONPATH)' >> $(VIRTUALENV_DIR)/bin/activate.fish - #echo 'functions -c deactivate old_deactivate' >> $(VIRTUALENV_DIR)/bin/activate.fish - #echo 'function deactivate' >> $(VIRTUALENV_DIR)/bin/activate.fish - #echo ' if test -n $$_OLD_PYTHONPATH' >> $(VIRTUALENV_DIR)/bin/activate.fish - #echo ' set -gx PYTHONPATH $$_OLD_PYTHONPATH' >> $(VIRTUALENV_DIR)/bin/activate.fish - #echo ' set -e _OLD_PYTHONPATH' >> $(VIRTUALENV_DIR)/bin/activate.fish - #echo ' end' >> $(VIRTUALENV_DIR)/bin/activate.fish - #echo ' old_deactivate' >> $(VIRTUALENV_DIR)/bin/activate.fish - #echo ' functions -e old_deactivate' >> $(VIRTUALENV_DIR)/bin/activate.fish - #echo 'end' >> $(VIRTUALENV_DIR)/bin/activate.fish - #touch $(VIRTUALENV_DIR)/bin/activate.fish - # debug pip installed packages $(VIRTUALENV_DIR)/bin/pip list @@ -819,7 +781,7 @@ unit-tests: requirements .unit-tests @echo @echo "----- Dropping st2-test db -----" @mongosh st2-test --eval "db.dropDatabase();" - @failed=0; \ + @failed=(); \ for component in $(COMPONENTS_TEST); do\ echo "==========================================================="; \ echo "Running tests in" $$component; \ @@ -828,13 +790,13 @@ unit-tests: requirements .unit-tests ST2TESTS_REDIS_HOST=$(ST2TESTS_REDIS_HOST) \ ST2TESTS_REDIS_PORT=$(ST2TESTS_REDIS_PORT) \ pytest -rx --verbose \ - $$component/tests/unit || ((failed+=1)); \ + $$component/tests/unit || failed+=($$component); \ echo "-----------------------------------------------------------"; \ echo "Done running tests in" $$component; \ echo "==========================================================="; \ done; \ - echo pytest runs failed=$$failed; \ - if [ $$failed -gt 0 ]; then exit 1; fi + echo "pytest runs failed=$${failed[@]}"; \ + if [ $${#failed[@]} -gt 0 ]; then exit 1; fi .PHONY: .run-unit-tests-coverage ifdef INCLUDE_TESTS_IN_COVERAGE @@ -846,7 +808,7 @@ endif @echo @echo "----- Dropping st2-test db -----" @mongosh st2-test --eval "db.dropDatabase();" - failed=0; \ + failed=(); \ for component in $(COMPONENTS_TEST); do\ echo "==========================================================="; \ echo "Running tests in" $$component; \ @@ -856,13 +818,13 @@ endif ST2TESTS_REDIS_PORT=$(ST2TESTS_REDIS_PORT) \ COVERAGE_FILE=.coverage.unit.$$(echo $$component | tr '/' '.') \ pytest --verbose $(PYTEST_OPTS) --cov=$$component --cov-branch \ - $$component/tests/unit || ((failed+=1)); \ + $$component/tests/unit || failed+=($$component); \ echo "-----------------------------------------------------------"; \ echo "Done running tests in" $$component; \ echo "==========================================================="; \ done; \ - echo pytest runs failed=$$failed; \ - if [ $$failed -gt 0 ]; then exit 1; fi + echo "pytest runs failed=$${failed[@]}"; \ + if [ $${#failed[@]} -gt 0 ]; then exit 1; fi .PHONY: .combine-unit-tests-coverage .combine-unit-tests-coverage: .run-unit-tests-coverage @@ -907,20 +869,20 @@ itests: requirements .itests @echo @echo "----- Dropping st2-test db -----" @mongosh st2-test --eval "db.dropDatabase();" - @failed=0; \ + @failed=(); \ for component in $(COMPONENTS_TEST); do\ echo "==========================================================="; \ echo "Running integration tests in" $$component; \ echo "-----------------------------------------------------------"; \ . $(VIRTUALENV_DIR)/bin/activate; \ pytest --capture=no --verbose $(PYTEST_OPTS) \ - $$component/tests/integration || ((failed+=1)); \ + $$component/tests/integration || failed+=($$component); \ echo "-----------------------------------------------------------"; \ echo "Done running integration tests in" $$component; \ echo "==========================================================="; \ done; \ - echo pytest runs failed=$$failed; \ - if [ $$failed -gt 0 ]; then exit 1; fi + echo "pytest runs failed=$${failed[@]}"; \ + if [ $${#failed[@]} -gt 0 ]; then exit 1; fi .PHONY: .run-integration-tests-coverage ifdef INCLUDE_TESTS_IN_COVERAGE @@ -932,7 +894,7 @@ endif @echo @echo "----- Dropping st2-test db -----" @mongosh st2-test --eval "db.dropDatabase();" - @failed=0; \ + @failed=(); \ for component in $(COMPONENTS_TEST); do\ echo "==========================================================="; \ echo "Running integration tests in" $$component; \ @@ -940,13 +902,13 @@ endif . $(VIRTUALENV_DIR)/bin/activate; \ COVERAGE_FILE=.coverage.integration.$$(echo $$component | tr '/' '.') \ pytest --capture=no --verbose $(PYTEST_OPTS) --cov=$$component --cov-branch \ - $$component/tests/integration || ((failed+=1)); \ + $$component/tests/integration || failed+=($$component); \ echo "-----------------------------------------------------------"; \ echo "Done integration running tests in" $$component; \ echo "==========================================================="; \ done; \ - echo pytest runs failed=$$failed; \ - if [ $$failed -gt 0 ]; then exit 1; fi + echo "pytest runs failed=$${failed[@]}"; \ + if [ $${#failed[@]} -gt 0 ]; then exit 1; fi # NOTE: If you also want to run orquesta tests which seem to have a bunch of race conditions, use # ci-integration-full target # @echo @@ -1015,13 +977,6 @@ endif make .coverage-combine; \ fi -# @for coverage_result in $(COVERAGE_GLOBS); do \ -# [ -e $${coverage_result} ] || echo "$${coverage_result} does not exist." && continue; \ -# echo "Combining data from $${coverage_result}"; \ -# . $(VIRTUALENV_DIR)/bin/activate; coverage combine $${coverage_result}; \ -# done || \ -# (echo "Running .coverage-combine"; make .coverage-combine) - .PHONY: .coverage-report .coverage-report: .coverage . $(VIRTUALENV_DIR)/bin/activate; coverage report @@ -1058,7 +1013,7 @@ packs-tests: requirements .packs-tests @echo "==================== packs-tests ====================" @echo # Install st2common to register metrics drivers - (cd ${ROOT_DIR}/st2common; ${ROOT_DIR}/$(VIRTUALENV_DIR)/bin/python setup.py develop --no-deps) + (cd ${ROOT_DIR}/st2common; ${ROOT_DIR}/$(VIRTUALENV_DIR)/bin/python -m pip install --editable . --no-deps) . $(VIRTUALENV_DIR)/bin/activate; find ${ROOT_DIR}/contrib/* -maxdepth 0 -type d -print0 | xargs -0 -I FILENAME ./st2common/bin/st2-run-pack-tests -c -t -x -p FILENAME @@ -1072,14 +1027,16 @@ runners-tests: requirements .runners-tests @echo @echo "----- Dropping st2-test db -----" @mongosh st2-test --eval "db.dropDatabase();" - @failed=0; \ + @failed=(); \ for component in $(COMPONENTS_RUNNERS); do\ echo "==========================================================="; \ echo "Running tests in" $$component; \ echo "==========================================================="; \ - . $(VIRTUALENV_DIR)/bin/activate; pytest --capture=no --verbose $(PYTEST_OPTS) $$component/tests/unit || ((failed+=1)); \ + . $(VIRTUALENV_DIR)/bin/activate; \ + pytest --capture=no --verbose $(PYTEST_OPTS) $$component/tests/unit || failed+=($$component); \ done; \ - if [ $$failed -gt 0 ]; then exit 1; fi + echo "pytest runs failed=$${failed[@]}"; \ + if [ $${#failed[@]} -gt 0 ]; then exit 1; fi .PHONY: runners-itests runners-itests: requirements .runners-itests @@ -1090,15 +1047,16 @@ runners-itests: requirements .runners-itests @echo "==================== runners-itests ====================" @echo @echo "----- Dropping st2-test db -----" - @failed=0; \ + @failed=(); \ for component in $(COMPONENTS_RUNNERS); do\ echo "==========================================================="; \ echo "Running integration tests in" $$component; \ echo "==========================================================="; \ - . $(VIRTUALENV_DIR)/bin/activate; pytest --capture=no --verbose $(PYTEST_OPTS) $$component/tests/integration || ((failed+=1)); \ + . $(VIRTUALENV_DIR)/bin/activate; \ + pytest --capture=no --verbose $(PYTEST_OPTS) $$component/tests/integration || failed+=($$component); \ done; \ - echo pytest runs failed=$$failed; \ - if [ $$failed -gt 0 ]; then exit 1; fi + echo "pytest runs failed=$${failed[@]}"; \ + if [ $${#failed[@]} -gt 0 ]; then exit 1; fi .PHONY: .runners-itests-coverage-html .runners-itests-coverage-html: @@ -1106,23 +1064,23 @@ runners-itests: requirements .runners-itests @echo "============== runners-itests-coverage-html ==============" @echo @echo "The tests assume st2 is running on 127.0.0.1." - @failed=0; \ + @failed=(); \ for component in $(COMPONENTS_RUNNERS); do\ echo "==========================================================="; \ echo "Running integration tests in" $$component; \ echo "==========================================================="; \ . $(VIRTUALENV_DIR)/bin/activate; pytest --capture=no --verbose $(PYTEST_OPTS) \ - --cov=$$component --cov-report=html $$component/tests/integration || ((failed+=1)); \ + --cov=$$component --cov-report=html $$component/tests/integration || failed+=($$component); \ done; \ - echo pytest runs failed=$$failed; \ - if [ $$failed -gt 0 ]; then exit 1; fi + echo "pytest runs failed=$${failed[@]}"; \ + if [ $${#failed[@]} -gt 0 ]; then exit 1; fi .PHONY: cli cli: @echo @echo "=================== Building st2 client ===================" @echo - pushd $(CURDIR) && cd st2client && ((python setup.py develop || printf "\n\n!!! ERROR: BUILD FAILED !!!\n") || popd) + pushd $(CURDIR) && cd st2client && ((python -m pip install --editable . || printf "\n\n!!! ERROR: BUILD FAILED !!!\n") || popd) .PHONY: rpms rpms: diff --git a/fixed-requirements.txt b/fixed-requirements.txt index 6bb2f93314..226d1da937 100644 --- a/fixed-requirements.txt +++ b/fixed-requirements.txt @@ -11,15 +11,16 @@ apscheduler==3.11.0 chardet==5.2.0 cffi==1.17.1 cryptography==43.0.3 -eventlet==0.39.1 +eventlet==0.40.4 flex==6.14.1 # Note: installs gitpython==3.1.37 (security fixed) under py3.8 and gitpython==3.1.18 (latest available, vulnerable) under py3.6 # TODO: Pin to 3.1.37 or higher after dropping python3.6 support gitpython==3.1.45 # Needed by gitpython, old versions used to bundle it gitdb==4.0.12 -# Note: greenlet is used by eventlet -greenlet==3.1.1 +gevent==25.9.1 +# Note: greenlet is used by eventlet & gevent (3.2.2 is the minimum common version) +greenlet==3.2.2 gunicorn==23.0.0 jsonpath-rw==1.4.0 jsonschema==3.2.0 diff --git a/requirements.txt b/requirements.txt index 6493a24be9..11bae15fb6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,11 +18,12 @@ cryptography==43.0.3 decorator==5.2.1 dnspython editor==1.6.6 -eventlet==0.39.1 +eventlet==0.40.4 flex==6.14.1 +gevent==25.9.1 gitdb==4.0.12 gitpython==3.1.45 -greenlet==3.1.1 +greenlet==3.2.2 gunicorn==23.0.0 importlib-metadata==8.5.0 jinja2==3.1.6 diff --git a/st2actions/in-requirements.txt b/st2actions/in-requirements.txt index 0d5630871e..df71e71e3a 100644 --- a/st2actions/in-requirements.txt +++ b/st2actions/in-requirements.txt @@ -2,6 +2,7 @@ apscheduler python-dateutil eventlet +gevent jinja2 kombu oslo.config diff --git a/st2actions/requirements.txt b/st2actions/requirements.txt index befa78509a..b1615cef15 100644 --- a/st2actions/requirements.txt +++ b/st2actions/requirements.txt @@ -8,7 +8,8 @@ apscheduler==3.11.0 backports.zoneinfo[tzdata]; python_version<"3.9" chardet==5.2.0 -eventlet==0.39.1 +eventlet==0.40.4 +gevent==25.9.1 gitpython==3.1.45 jinja2==3.1.6 kombu==5.5.4 diff --git a/st2api/in-requirements.txt b/st2api/in-requirements.txt index 25c9369f60..f2f6d5cdb3 100644 --- a/st2api/in-requirements.txt +++ b/st2api/in-requirements.txt @@ -1,5 +1,6 @@ # Remember to list implicit packages here, otherwise version won't be fixated! eventlet +gevent jsonschema kombu mongoengine diff --git a/st2api/requirements.txt b/st2api/requirements.txt index ac14d6dd24..4bd8e9fcf5 100644 --- a/st2api/requirements.txt +++ b/st2api/requirements.txt @@ -6,7 +6,8 @@ # in-requirements.txt for that component and then run 'make requirements' to # update the component requirements.txt backports.zoneinfo[tzdata]; python_version<"3.9" -eventlet==0.39.1 +eventlet==0.40.4 +gevent==25.9.1 gunicorn==23.0.0 jsonschema==3.2.0 kombu==5.5.4 diff --git a/st2auth/in-requirements.txt b/st2auth/in-requirements.txt index 94e873fe41..d635ec8ddf 100644 --- a/st2auth/in-requirements.txt +++ b/st2auth/in-requirements.txt @@ -1,6 +1,7 @@ # Remember to list implicit packages here, otherwise version won't be fixated! bcrypt eventlet +gevent oslo.config pymongo six diff --git a/st2auth/requirements.txt b/st2auth/requirements.txt index 19dbe7456f..4636d5bb55 100644 --- a/st2auth/requirements.txt +++ b/st2auth/requirements.txt @@ -6,7 +6,8 @@ # in-requirements.txt for that component and then run 'make requirements' to # update the component requirements.txt bcrypt==5.0.0 -eventlet==0.39.1 +eventlet==0.40.4 +gevent==25.9.1 gunicorn==23.0.0 oslo.config==9.6.0 pymongo==4.6.3 diff --git a/st2common/in-requirements.txt b/st2common/in-requirements.txt index c8ba49abe4..e097e6f52b 100644 --- a/st2common/in-requirements.txt +++ b/st2common/in-requirements.txt @@ -3,6 +3,7 @@ apscheduler dnspython python-dateutil eventlet +gevent # used by eventlet greenlet jinja2 diff --git a/st2common/requirements.txt b/st2common/requirements.txt index 4e4b644b2c..cdbec6ae64 100644 --- a/st2common/requirements.txt +++ b/st2common/requirements.txt @@ -14,11 +14,12 @@ ciso8601 cryptography==43.0.3 decorator==5.2.1 dnspython -eventlet==0.39.1 +eventlet==0.40.4 flex==6.14.1 +gevent==25.9.1 gitdb==4.0.12 gitpython==3.1.45 -greenlet==3.1.1 +greenlet==3.2.2 jinja2==3.1.6 jsonpath-rw==1.4.0 jsonschema==3.2.0 diff --git a/st2common/st2common/util/concurrency.py b/st2common/st2common/util/concurrency.py index f5a2212e43..150d088036 100644 --- a/st2common/st2common/util/concurrency.py +++ b/st2common/st2common/util/concurrency.py @@ -1,4 +1,4 @@ -# Copyright 2020 The StackStorm Authors. +# Copyright 2020-2026 The StackStorm Authors. # Copyright 2019 Extreme Networks, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,7 +17,7 @@ Module which acts as a compatibility later between eventlet and gevent. It dispatches function call to the concurrency library which is configured using -"set_concurrency_library" functoion. +"set_concurrency_library" function. """ try: @@ -168,7 +168,7 @@ def green_pool_wait_all(pool): if CONCURRENCY_LIBRARY == "eventlet": return pool.waitall() elif CONCURRENCY_LIBRARY == "gevent": - # NOTE: This mimicks eventlet.waitall() functionallity better than + # NOTE: This mimicks eventlet.waitall() functionality better than # pool.join() return all(gl.ready() for gl in pool.greenlets) else: diff --git a/st2common/st2common/util/monkey_patch.py b/st2common/st2common/util/monkey_patch.py index ceb430592d..2694d3d785 100644 --- a/st2common/st2common/util/monkey_patch.py +++ b/st2common/st2common/util/monkey_patch.py @@ -18,6 +18,7 @@ """ from __future__ import absolute_import +from st2common.util.concurrency import get_concurrency_library import os import sys @@ -45,15 +46,21 @@ def monkey_patch(patch_thread=None): patched unless debugger is used. :type patch_thread: ``bool`` """ - import eventlet + concurrency_library = get_concurrency_library() + if concurrency_library == "eventlet": + import eventlet - if patch_thread is None: - patch_thread = not is_use_debugger_flag_provided() + if patch_thread is None: + patch_thread = not is_use_debugger_flag_provided() - # TODO: support gevent.patch_all if .concurrency.CONCURRENCY_LIBRARY = "gevent" - eventlet.monkey_patch( - os=True, select=True, socket=True, thread=patch_thread, time=True - ) + eventlet.monkey_patch( + os=True, select=True, socket=True, thread=patch_thread, time=True + ) + elif concurrency_library == "gevent": + # TODO: support gevent.patch_all if .concurrency.CONCURRENCY_LIBRARY = "gevent" + raise NotImplementedError + else: + raise RuntimeError(f"Unsupported concurrency library {concurrency_library}") def use_select_poll_workaround(nose_only=True): @@ -79,30 +86,31 @@ def use_select_poll_workaround(nose_only=True): runner. :type nose_only: ``bool`` """ - import sys - import select - import subprocess - import eventlet - - # Work around to get tests to pass with eventlet >= 0.20.0 - if not nose_only or ( - nose_only - # sys._called_from_test set in conftest.py for pytest runs - and ("nose" in sys.modules.keys() or hasattr(sys, "_called_from_test")) - ): - # Add back blocking poll() to eventlet monkeypatched select - original_poll = eventlet.patcher.original("select").poll - select.poll = original_poll - - sys.modules["select"] = select - subprocess.select = select - - if sys.version_info >= (3, 6, 5): - # If we also don't patch selectors.select, it will fail with Python >= 3.6.5 - import selectors # pylint: disable=import-error - - sys.modules["selectors"] = selectors - selectors.select = sys.modules["select"] + if get_concurrency_library() == "eventlet": + import sys + import select + import subprocess + import eventlet + + # Work around to get tests to pass with eventlet >= 0.20.0 + if not nose_only or ( + nose_only + # sys._called_from_test set in conftest.py for pytest runs + and ("nose" in sys.modules.keys() or hasattr(sys, "_called_from_test")) + ): + # Add back blocking poll() to eventlet monkeypatched select + original_poll = eventlet.patcher.original("select").poll + select.poll = original_poll + + sys.modules["select"] = select + subprocess.select = select + + if sys.version_info >= (3, 6, 5): + # If we also don't patch selectors.select, it will fail with Python >= 3.6.5 + import selectors # pylint: disable=import-error + + sys.modules["selectors"] = selectors + selectors.select = sys.modules["select"] def is_use_debugger_flag_provided(): diff --git a/st2reactor/in-requirements.txt b/st2reactor/in-requirements.txt index e1bccfac12..a2865114e9 100644 --- a/st2reactor/in-requirements.txt +++ b/st2reactor/in-requirements.txt @@ -2,6 +2,7 @@ apscheduler python-dateutil eventlet +gevent jsonpath-rw jsonschema kombu diff --git a/st2reactor/requirements.txt b/st2reactor/requirements.txt index b275c50e22..02d034f9ba 100644 --- a/st2reactor/requirements.txt +++ b/st2reactor/requirements.txt @@ -7,7 +7,8 @@ # update the component requirements.txt apscheduler==3.11.0 backports.zoneinfo[tzdata]; python_version<"3.9" -eventlet==0.39.1 +eventlet==0.40.4 +gevent==25.9.1 jsonpath-rw==1.4.0 jsonschema==3.2.0 kombu==5.5.4 diff --git a/st2stream/in-requirements.txt b/st2stream/in-requirements.txt index 273a2b1b2f..7671b1712b 100644 --- a/st2stream/in-requirements.txt +++ b/st2stream/in-requirements.txt @@ -1,5 +1,6 @@ # Remember to list implicit packages here, otherwise version won't be fixated! eventlet +gevent jsonschema kombu mongoengine diff --git a/st2stream/requirements.txt b/st2stream/requirements.txt index b4c33bd8df..bc77e309b9 100644 --- a/st2stream/requirements.txt +++ b/st2stream/requirements.txt @@ -6,7 +6,8 @@ # in-requirements.txt for that component and then run 'make requirements' to # update the component requirements.txt backports.zoneinfo[tzdata]; python_version<"3.9" -eventlet==0.39.1 +eventlet==0.40.4 +gevent==25.9.1 gunicorn==23.0.0 jsonschema==3.2.0 kombu==5.5.4 From 75af8a30984d85c1e3bd78823e52816064f831c2 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Tue, 24 Feb 2026 12:16:33 -0700 Subject: [PATCH 02/22] wsgi WIP --- st2common/st2common/util/concurrency.py | 22 ++++++++++++++++++++++ st2common/st2common/util/monkey_patch.py | 7 +++++-- st2stream/st2stream/cmd/api.py | 22 ++++++++++++---------- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/st2common/st2common/util/concurrency.py b/st2common/st2common/util/concurrency.py index 150d088036..f064be4292 100644 --- a/st2common/st2common/util/concurrency.py +++ b/st2common/st2common/util/concurrency.py @@ -140,6 +140,16 @@ def get_greenlet_exit_exception_class(): raise ValueError("Unsupported concurrency library") +def get_default_green_pool_size(): + if CONCURRENCY_LIBRARY == "eventlet": + return eventlet.wsgi.DEFAULT_MAX_SIMULTANEOUS_REQUESTS + elif CONCURRENCY_LIBRARY == "gevent": + # matches what DEFAULT_MAX_SIMULTANEOUS_REQUESTS is for eventlet + return 1024 + else: + raise ValueError("Unsupported concurrency library") + + def get_green_pool_class(): if CONCURRENCY_LIBRARY == "eventlet": return eventlet.GreenPool @@ -173,3 +183,15 @@ def green_pool_wait_all(pool): return all(gl.ready() for gl in pool.greenlets) else: raise ValueError("Unsupported concurrency library") + +def listen_server(host, port): + if CONCURRENCY_LIBRARY == "eventlet": + return eventlet.listen((host, port)) + elif CONCURRENCY_LIBRARY == "gevent": + import socket + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind((host, port)) + return sock.listen(5) + else: + raise ValueError("Unsupported concurrency library") + diff --git a/st2common/st2common/util/monkey_patch.py b/st2common/st2common/util/monkey_patch.py index 2694d3d785..c852a48b2c 100644 --- a/st2common/st2common/util/monkey_patch.py +++ b/st2common/st2common/util/monkey_patch.py @@ -57,8 +57,11 @@ def monkey_patch(patch_thread=None): os=True, select=True, socket=True, thread=patch_thread, time=True ) elif concurrency_library == "gevent": - # TODO: support gevent.patch_all if .concurrency.CONCURRENCY_LIBRARY = "gevent" - raise NotImplementedError + # Match what eventlet was enabling, fallback on gevent defaults + import gevent + gevent.monkey.patch_all( + os=True, select=True, thread=True, time=True, socket=True + ) else: raise RuntimeError(f"Unsupported concurrency library {concurrency_library}") diff --git a/st2stream/st2stream/cmd/api.py b/st2stream/st2stream/cmd/api.py index 3de0c89f03..08caa0a2bd 100644 --- a/st2stream/st2stream/cmd/api.py +++ b/st2stream/st2stream/cmd/api.py @@ -21,6 +21,7 @@ import sys import eventlet + from oslo_config import cfg from eventlet import wsgi @@ -29,6 +30,7 @@ from st2common.service_setup import teardown as common_teardown from st2common.service_setup import deregister_service from st2common.stream.listener import get_listener_if_set +from st2common.util import concurrency from st2common.util.wsgi import shutdown_server_kill_pending_requests from st2stream.signal_handlers import register_stream_signal_handlers from st2stream import config @@ -40,13 +42,13 @@ __all__ = ["main"] -eventlet.monkey_patch( - os=True, - select=True, - socket=True, - thread=False if "--use-debugger" in sys.argv else True, - time=True, -) +#eventlet.monkey_patch( +# os=True, +# select=True, +# socket=True, +# thread=False if "--use-debugger" in sys.argv else True, +# time=True, +#) LOG = logging.getLogger(__name__) STREAM = "stream" @@ -83,9 +85,9 @@ def _run_server(): "(PID=%s) ST2 Stream API is serving on http://%s:%s.", os.getpid(), host, port ) - max_pool_size = eventlet.wsgi.DEFAULT_MAX_SIMULTANEOUS_REQUESTS - worker_pool = eventlet.GreenPool(max_pool_size) - sock = eventlet.listen((host, port)) + max_pool_size = concurrency.get_default_green_pool_size() + worker_pool = concurrency.get_green_pool_class()(max_pool_size) + sock = concurrency.listen_server(host, port) def queue_shutdown(signal_number, stack_frame): deregister_service(STREAM) From 020539c86805a0780a9c2a6e566361f624d1c004 Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 19 Feb 2026 21:53:54 +0100 Subject: [PATCH 03/22] Add gevent to pants requirements and regenerate lock file. --- lockfiles/st2.lock | 523 +++++++++++++++++++++++++++++++++-------- requirements-pants.txt | 3 +- 2 files changed, 422 insertions(+), 104 deletions(-) diff --git a/lockfiles/st2.lock b/lockfiles/st2.lock index 779589e997..427b424009 100644 --- a/lockfiles/st2.lock +++ b/lockfiles/st2.lock @@ -22,6 +22,7 @@ // "eventlet", // "flask", // "flex", +// "gevent", // "gitdb", // "gitpython", // "graphviz", @@ -133,13 +134,13 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "6162cb5683cb09923654fa9bdd3130c4be4bfda6ad8990971c9597ecd52965d2", - "url": "https://files.pythonhosted.org/packages/58/9f/d3c76f76c73fcc959d28e9def45b8b1cc3d7722660c5003b19c1022fd7f4/apscheduler-3.11.1-py3-none-any.whl" + "hash": "ce005177f741409db4e4dd40a7431b76feb856b9dd69d57e0da49d6715bfd26d", + "url": "https://files.pythonhosted.org/packages/9f/64/2e54428beba8d9992aa478bb8f6de9e4ecaa5f8f513bcfd567ed7fb0262d/apscheduler-3.11.2-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "0db77af6400c84d1747fe98a04b8b58f0080c77d11d338c4f507a9752880f221", - "url": "https://files.pythonhosted.org/packages/d0/81/192db4f8471de5bc1f0d098783decffb1e6e69c4f8b4bc6711094691950b/apscheduler-3.11.1.tar.gz" + "hash": "2a9966b052ec805f020c8c4c3ae6e6a06e24b1bf19f2e11d91d8cca0473eef41", + "url": "https://files.pythonhosted.org/packages/07/12/3e4389e5920b4c1763390c6d371162f3784f86f85cd6d6c1bfe68eef14e2/apscheduler-3.11.2.tar.gz" } ], "project_name": "apscheduler", @@ -155,6 +156,7 @@ "packaging; extra == \"doc\"", "protobuf<=3.21.0; extra == \"etcd\"", "pymongo>=3.0; extra == \"mongodb\"", + "pytest-timeout; extra == \"test\"", "pytest; extra == \"test\"", "pytz; extra == \"test\"", "redis>=3.0; extra == \"redis\"", @@ -168,7 +170,7 @@ "tzlocal>=3.0" ], "requires_python": ">=3.8", - "version": "3.11.1" + "version": "3.11.2" }, { "artifacts": [ @@ -542,19 +544,19 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", - "url": "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl" + "hash": "9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", + "url": "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", - "url": "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz" + "hash": "ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", + "url": "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz" } ], "project_name": "certifi", "requires_dists": [], "requires_python": ">=3.7", - "version": "2025.11.12" + "version": "2026.1.4" }, { "artifacts": [ @@ -1878,6 +1880,169 @@ "requires_python": ">=3.8", "version": "3.0.0" }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "7b00f8c9065de3ad226f7979154a7b27f3b9151c8055c162332369262fc025d8", + "url": "https://files.pythonhosted.org/packages/ae/15/c1cd1f2005f457028ecde345260fc4ab2197c6b660a8f3729784a6a903ca/gevent-24.2.1-pp310-pypy310_pp73-macosx_11_0_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "8f4b8e777d39013595a7740b4463e61b1cfe5f462f1b609b28fbc1e4c4ff01e5", + "url": "https://files.pythonhosted.org/packages/07/5a/a0b6c4cdd0917137c587edaba76b6c679181e10d25405247d2f5d8a2751d/gevent-24.2.1-cp38-cp38-macosx_11_0_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "cdf66977a976d6a3cfb006afdf825d1482f84f7b81179db33941f2fc9673bb1d", + "url": "https://files.pythonhosted.org/packages/15/12/7c91964af7112b3b435aa836401d8ca212ba9d43bcfea34c770b73515740/gevent-24.2.1-cp311-cp311-musllinux_1_1_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "6f947a9abc1a129858391b3d9334c45041c08a0f23d14333d5b844b6e5c17a07", + "url": "https://files.pythonhosted.org/packages/15/9e/e775a6b261bd871f37a2aae4c335d150f2c64c54c166e8dd8cf63210b445/gevent-24.2.1-cp310-cp310-macosx_11_0_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "ca80b121bbec76d7794fcb45e65a7eca660a76cc1a104ed439cdbd7df5f0b060", + "url": "https://files.pythonhosted.org/packages/1e/0f/66b517209682f7ec2863fd6ea13e26cc015d3c7e12c0acbd19d14cc67ac8/gevent-24.2.1-cp310-cp310-manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "141a2b24ad14f7b9576965c0c84927fc85f824a9bb19f6ec1e61e845d87c9cd8", + "url": "https://files.pythonhosted.org/packages/20/4d/0972d1ff47f118aeb32d0b33b50aed73583c31238dc063cb5ba230acbe38/gevent-24.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "432fc76f680acf7cf188c2ee0f5d3ab73b63c1f03114c7cd8a34cebbe5aa2056", + "url": "https://files.pythonhosted.org/packages/27/24/a3a7b713acfcf1177207f49ec25c665123f8972f42bee641bcc9f32961f4/gevent-24.2.1.tar.gz" + }, + { + "algorithm": "sha256", + "hash": "d7f87c2c02e03d99b95cfa6f7a776409083a9e4d468912e18c7680437b29222c", + "url": "https://files.pythonhosted.org/packages/40/9c/8880eef385b31f694222f5c94b2b487a8b37b99aceeed3e93cb0cb038511/gevent-24.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "f8bb35ce57a63c9a6896c71a285818a3922d8ca05d150fd1fe49a7f57287b836", + "url": "https://files.pythonhosted.org/packages/4a/db/64295bfd9a51874b715e82ba5ab971f2c298cf283297e4cf5bec37db17d9/gevent-24.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "f5e8e8d60e18d5f7fd49983f0c4696deeddaf6e608fbab33397671e2fcc6cc91", + "url": "https://files.pythonhosted.org/packages/58/b8/aaf9ff71ba9a7012e04400726b0e0e6986460030dfae3168482069422305/gevent-24.2.1-cp311-cp311-manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "90cbac1ec05b305a1b90ede61ef73126afdeb5a804ae04480d6da12c56378df1", + "url": "https://files.pythonhosted.org/packages/5f/67/c2e3b6f45f77019a9bec6e594f1abede96fd2cd9292024cc6a334648b5e0/gevent-24.2.1-cp39-cp39-musllinux_1_1_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "a7ceb59986456ce851160867ce4929edaffbd2f069ae25717150199f8e1548b8", + "url": "https://files.pythonhosted.org/packages/63/11/9f67d737a64217649460b2654b595afd9a2565d20688d92c18b17e522ec5/gevent-24.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "03aa5879acd6b7076f6a2a307410fb1e0d288b84b03cdfd8c74db8b4bc882fc5", + "url": "https://files.pythonhosted.org/packages/64/34/e561fb53ec80e81a83b76667c004c838a292dde8adf80ff289558b4a4df8/gevent-24.2.1-cp311-cp311-macosx_11_0_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "b9913c45d1be52d7a5db0c63977eebb51f68a2d5e6fd922d1d9b5e5fd758cc98", + "url": "https://files.pythonhosted.org/packages/6b/ee/883de5d784d5ffbb349549be82b805d668a841c2bb2b17bb294af2740d16/gevent-24.2.1-cp310-cp310-musllinux_1_1_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "14532a67f7cb29fb055a0e9b39f16b88ed22c66b96641df8c04bdc38c26b9ea5", + "url": "https://files.pythonhosted.org/packages/6b/f5/14d4085bb7774ed6cb84d9fd2360a9b3a99a502183b4979c8cad253dfba2/gevent-24.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "fbfdce91239fe306772faab57597186710d5699213f4df099d1612da7320d682", + "url": "https://files.pythonhosted.org/packages/74/ee/6febc62ddd399b0f060785bea8ae3c994ce47dfe6ec46ece3b1a90cc496b/gevent-24.2.1-cp311-cp311-musllinux_1_1_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "2ae3a25ecce0a5b0cd0808ab716bfca180230112bb4bc89b46ae0061d62d4afe", + "url": "https://files.pythonhosted.org/packages/78/23/328809bc89c21669434fddaa863c33008486a423eb7ea049b2bf82ae154b/gevent-24.2.1-cp39-cp39-macosx_11_0_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "2955eea9c44c842c626feebf4459c42ce168685aa99594e049d03bedf53c2800", + "url": "https://files.pythonhosted.org/packages/7a/1c/528238b5460dfcd16a76f4ab7837d6fef899fbf0666c248891efb21b0829/gevent-24.2.1-cp38-cp38-musllinux_1_1_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "918cdf8751b24986f915d743225ad6b702f83e1106e08a63b736e3a4c6ead789", + "url": "https://files.pythonhosted.org/packages/7c/27/a0eee37ba204411c48744b6cfbb79afd01e50185c3cd91421948f1cc40f1/gevent-24.2.1-cp310-cp310-musllinux_1_1_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "2e9ac06f225b696cdedbb22f9e805e2dd87bf82e8fa5e17756f94e88a9d37cf7", + "url": "https://files.pythonhosted.org/packages/7f/1f/b9b5b38c65e8a69fedb11b43ba3c824b164dde21ffa19491e1e866876c8b/gevent-24.2.1-cp39-cp39-manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "dd23df885318391856415e20acfd51a985cba6919f0be78ed89f5db9ff3a31cb", + "url": "https://files.pythonhosted.org/packages/8c/ab/348bc172ef72f82c5684764887d4a5751200dad2ce772b164e120dd489ee/gevent-24.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "968581d1717bbcf170758580f5f97a2925854943c45a19be4d47299507db2eb7", + "url": "https://files.pythonhosted.org/packages/9c/0e/bf924a9998137d51e8ba84bd600ff5de17e405284811b26307748c0e0f9b/gevent-24.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "7899a38d0ae7e817e99adb217f586d0a4620e315e4de577444ebeeed2c5729be", + "url": "https://files.pythonhosted.org/packages/a1/bc/0f776a3f5a3c57e3f6bbe8abc3d39cc591f58aa03808b50af4f73ae4b238/gevent-24.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "5a1df555431f5cd5cc189a6ee3544d24f8c52f2529134685f1e878c4972ab026", + "url": "https://files.pythonhosted.org/packages/ca/0d/28048ce07ffb9cabf974583092bcb6008b8c55f880609f1515a085adb1f9/gevent-24.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "9202f22ef811053077d01f43cc02b4aaf4472792f9fd0f5081b0b05c926cca19", + "url": "https://files.pythonhosted.org/packages/e7/26/f7349b02cb06c87b2e5eb4547a33b3c5171076460bc45e18ec723d84320d/gevent-24.2.1-cp38-cp38-manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "bde283313daf0b34a8d1bab30325f5cb0f4e11b5869dbe5bc61f8fe09a8f66f3", + "url": "https://files.pythonhosted.org/packages/eb/6b/396ef229ee05286b957915cb3d96c8ff28793b2f21508ee4b6e51e207bbc/gevent-24.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + } + ], + "project_name": "gevent", + "requires_dists": [ + "cffi>=1.12.2; platform_python_implementation == \"CPython\" and extra == \"recommended\"", + "cffi>=1.12.2; platform_python_implementation == \"CPython\" and extra == \"test\"", + "cffi>=1.12.2; platform_python_implementation == \"CPython\" and sys_platform == \"win32\"", + "coverage>=5.0; sys_platform != \"win32\" and extra == \"test\"", + "dnspython<2.0,>=1.16.0; python_version < \"3.10\" and extra == \"dnspython\"", + "dnspython<2.0,>=1.16.0; python_version < \"3.10\" and extra == \"recommended\"", + "dnspython<2.0,>=1.16.0; python_version < \"3.10\" and extra == \"test\"", + "furo; extra == \"docs\"", + "greenlet>=2.0.0; platform_python_implementation == \"CPython\" and python_version < \"3.11\"", + "greenlet>=3.0rc3; platform_python_implementation == \"CPython\" and python_version >= \"3.11\"", + "idna; python_version < \"3.10\" and extra == \"dnspython\"", + "idna; python_version < \"3.10\" and extra == \"recommended\"", + "idna; python_version < \"3.10\" and extra == \"test\"", + "objgraph; extra == \"test\"", + "psutil>=5.7.0; (sys_platform != \"win32\" or platform_python_implementation == \"CPython\") and extra == \"monitor\"", + "psutil>=5.7.0; (sys_platform != \"win32\" or platform_python_implementation == \"CPython\") and extra == \"recommended\"", + "psutil>=5.7.0; (sys_platform != \"win32\" or platform_python_implementation == \"CPython\") and extra == \"test\"", + "repoze.sphinx.autointerface; extra == \"docs\"", + "requests; extra == \"test\"", + "sphinx; extra == \"docs\"", + "sphinxcontrib-programoutput; extra == \"docs\"", + "zope.event", + "zope.interface", + "zope.schema; extra == \"docs\"" + ], + "requires_python": ">=3.8", + "version": "24.2.1" + }, { "artifacts": [ { @@ -1902,13 +2067,13 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", - "url": "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl" + "hash": "79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", + "url": "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c", - "url": "https://files.pythonhosted.org/packages/9a/c8/dd58967d119baab745caec2f9d853297cec1989ec1d63f677d3880632b88/gitpython-3.1.45.tar.gz" + "hash": "400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", + "url": "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz" } ], "project_name": "gitpython", @@ -1917,7 +2082,7 @@ "ddt!=1.4.3,>=1.1.1; extra == \"test\"", "gitdb<5,>=4.0.1", "mock; python_version < \"3.8\" and extra == \"test\"", - "mypy; extra == \"test\"", + "mypy==1.18.2; python_version >= \"3.9\" and extra == \"test\"", "pre-commit; extra == \"test\"", "pytest-cov; extra == \"test\"", "pytest-instafail; extra == \"test\"", @@ -1931,7 +2096,7 @@ "typing-extensions>=3.10.0.2; python_version < \"3.10\"" ], "requires_python": ">=3.7", - "version": "3.1.45" + "version": "3.1.46" }, { "artifacts": [ @@ -2176,39 +2341,39 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "b9cd78abea9b4e43a7714c6e0f8b6b8561a6fc1e95d5dbd367f5bf0ef35f5d24", - "url": "https://files.pythonhosted.org/packages/8c/a2/0d269db0f6163be503775dc8b6a6fa15820cc9fdc866f6ba608d86b721f2/httplib2-0.31.0-py3-none-any.whl" + "hash": "dbf0c2fa3862acf3c55c078ea9c0bc4481d7dc5117cae71be9514912cf9f8349", + "url": "https://files.pythonhosted.org/packages/2f/90/fd509079dfcab01102c0fdd87f3a9506894bc70afcf9e9785ef6b2b3aff6/httplib2-0.31.2-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "ac7ab497c50975147d4f7b1ade44becc7df2f8954d42b38b3d69c515f531135c", - "url": "https://files.pythonhosted.org/packages/52/77/6653db69c1f7ecfe5e3f9726fdadc981794656fcd7d98c4209fecfea9993/httplib2-0.31.0.tar.gz" + "hash": "385e0869d7397484f4eab426197a4c020b606edd43372492337c0b4010ae5d24", + "url": "https://files.pythonhosted.org/packages/c1/1f/e86365613582c027dda5ddb64e1010e57a3d53e99ab8a72093fa13d565ec/httplib2-0.31.2.tar.gz" } ], "project_name": "httplib2", "requires_dists": [ - "pyparsing<4,>=3.0.4" + "pyparsing<4,>=3.1" ], "requires_python": ">=3.6", - "version": "0.31.0" + "version": "0.31.2" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "f05d1b3623223dd1c70f7848da7d699de3d9a2550b902a8234d9026292fb5762", - "url": "https://files.pythonhosted.org/packages/7c/2a/b3178baa75a3ec75a33588252296c82a1332d2b83cd01061539b74bde9dd/icdiff-2.0.7-py3-none-any.whl" + "hash": "fd8ce592d94261c435e2a410f668a41ec3be4527878f8df01100797021d8edb0", + "url": "https://files.pythonhosted.org/packages/85/4b/bb94514476624bfff977599e5ef3ac9aea603abb693639ed305b22ed1b35/icdiff-2.0.10-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "f79a318891adbf59a45e3a7694f5e1f18c5407065264637072ac8363b759866f", - "url": "https://files.pythonhosted.org/packages/fa/e4/43341832be5f2bcae71eb3ef08a07aaef9b74f74fe0b3675f62bd12057fe/icdiff-2.0.7.tar.gz" + "hash": "75a3de5c9af35ab45fb0504df59770c514a12c0d2b2c99e5f9c5c2429957e133", + "url": "https://files.pythonhosted.org/packages/5b/6d/41c8ff13b67c30ceb9325398ff6bb9fc4b77208c6c1d79db8d5913840bc6/icdiff-2.0.10.tar.gz" } ], "project_name": "icdiff", "requires_dists": [], "requires_python": null, - "version": "2.0.7" + "version": "2.0.10" }, { "artifacts": [ @@ -3455,19 +3620,19 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", - "url": "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl" + "hash": "b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", + "url": "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", - "url": "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz" + "hash": "00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", + "url": "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz" } ], "project_name": "packaging", "requires_dists": [], "requires_python": ">=3.8", - "version": "25.0" + "version": "26.0" }, { "artifacts": [ @@ -3729,28 +3894,38 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "56d974e02ca2c8eb4812c3f76c30e28836fffc311d55d979f1465c1feeb2b68b", - "url": "https://files.pythonhosted.org/packages/e0/95/992c8816a74016eb095e73585d747e0a8ea21a061ed3689474fabb29a395/psutil-7.1.3-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", + "url": "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", + "url": "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", + "url": "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "bc31fa00f1fbc3c3802141eede66f3a2d51d89716a194bf2cd6fc68310a19880", - "url": "https://files.pythonhosted.org/packages/68/3a/9f93cff5c025029a36d9a92fef47220ab4692ee7f2be0fba9f92813d0cb8/psutil-7.1.3-cp36-abi3-macosx_11_0_arm64.whl" + "hash": "fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", + "url": "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "3bb428f9f05c1225a558f53e30ccbad9930b11c3fc206836242de1091d3e7dd3", - "url": "https://files.pythonhosted.org/packages/ce/b1/5f49af514f76431ba4eea935b8ad3725cdeb397e9245ab919dbc1d1dc20f/psutil-7.1.3-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl" + "hash": "0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", + "url": "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz" }, { "algorithm": "sha256", - "hash": "6c86281738d77335af7aec228328e944b30930899ea760ecf33a4dba66be5e74", - "url": "https://files.pythonhosted.org/packages/e1/88/bdd0a41e5857d5d703287598cbf08dad90aed56774ea52ae071bae9071b6/psutil-7.1.3.tar.gz" + "hash": "076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", + "url": "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "2bdbcd0e58ca14996a42adf3621a6244f1bb2e2e528886959c72cf1e326677ab", - "url": "https://files.pythonhosted.org/packages/ef/94/46b9154a800253e7ecff5aaacdf8ebf43db99de4a2dfa18575b02548654e/psutil-7.1.3-cp36-abi3-macosx_10_9_x86_64.whl" + "hash": "ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", + "url": "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl" } ], "project_name": "psutil", @@ -3761,21 +3936,21 @@ "colorama; os_name == \"nt\" and extra == \"dev\"", "coverage; extra == \"dev\"", "packaging; extra == \"dev\"", + "psleak; extra == \"dev\"", + "psleak; extra == \"test\"", "pylint; extra == \"dev\"", "pyperf; extra == \"dev\"", "pypinfo; extra == \"dev\"", - "pyreadline; os_name == \"nt\" and extra == \"dev\"", + "pyreadline3; os_name == \"nt\" and extra == \"dev\"", "pytest-cov; extra == \"dev\"", "pytest-instafail; extra == \"dev\"", "pytest-instafail; extra == \"test\"", - "pytest-subtests; extra == \"dev\"", - "pytest-subtests; extra == \"test\"", "pytest-xdist; extra == \"dev\"", "pytest-xdist; extra == \"test\"", "pytest; extra == \"dev\"", "pytest; extra == \"test\"", - "pywin32; (os_name == \"nt\" and platform_python_implementation != \"PyPy\") and extra == \"dev\"", - "pywin32; (os_name == \"nt\" and platform_python_implementation != \"PyPy\") and extra == \"test\"", + "pywin32; (os_name == \"nt\" and implementation_name != \"pypy\") and extra == \"dev\"", + "pywin32; (os_name == \"nt\" and implementation_name != \"pypy\") and extra == \"test\"", "requests; extra == \"dev\"", "rstcheck; extra == \"dev\"", "ruff; extra == \"dev\"", @@ -3788,14 +3963,14 @@ "validate-pyproject[all]; extra == \"dev\"", "virtualenv; extra == \"dev\"", "vulture; extra == \"dev\"", - "wheel; (os_name == \"nt\" and platform_python_implementation != \"PyPy\") and extra == \"dev\"", - "wheel; (os_name == \"nt\" and platform_python_implementation != \"PyPy\") and extra == \"test\"", + "wheel; (os_name == \"nt\" and implementation_name != \"pypy\") and extra == \"dev\"", + "wheel; (os_name == \"nt\" and implementation_name != \"pypy\") and extra == \"test\"", "wheel; extra == \"dev\"", - "wmi; (os_name == \"nt\" and platform_python_implementation != \"PyPy\") and extra == \"dev\"", - "wmi; (os_name == \"nt\" and platform_python_implementation != \"PyPy\") and extra == \"test\"" + "wmi; (os_name == \"nt\" and implementation_name != \"pypy\") and extra == \"dev\"", + "wmi; (os_name == \"nt\" and implementation_name != \"pypy\") and extra == \"test\"" ], "requires_python": ">=3.6", - "version": "7.1.3" + "version": "7.2.2" }, { "artifacts": [ @@ -3837,19 +4012,19 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", - "url": "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl" + "hash": "1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", + "url": "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", - "url": "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz" + "hash": "9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", + "url": "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz" } ], "project_name": "pyasn1", "requires_dists": [], "requires_python": ">=3.8", - "version": "0.6.1" + "version": "0.6.2" }, { "artifacts": [ @@ -5327,13 +5502,13 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "90ab613b6583fc02d5369cbca13ea26ea0e182d1df2d943ee9cbe81d4c61add9", - "url": "https://files.pythonhosted.org/packages/15/65/3f0dba35760d902849d39d38c0a72767794b1963227b69a587f8a336d08c/setuptools-75.3.2-py3-none-any.whl" + "hash": "2dd50a7f42dddfa1d02a36f275dbe716f38ed250224f609d35fb60a09593d93e", + "url": "https://files.pythonhosted.org/packages/cd/b1/961ba076c7d3732e3a97e6681c55ef647afb795fe8bfcd27becec8a762ce/setuptools-75.3.4-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "3c1383e1038b68556a382c1e8ded8887cd20141b0eb5708a6c8d277de49364f5", - "url": "https://files.pythonhosted.org/packages/5c/01/771ea46cce201dd42cff043a5eea929d1c030fb3d1c2ee2729d02ca7814c/setuptools-75.3.2.tar.gz" + "hash": "b4ea3f76e1633c4d2d422a5d68ab35fd35402ad71e6acaa5d7e5956eb47e8887", + "url": "https://files.pythonhosted.org/packages/0e/93/d2622cbf262418995140dfc1ddb890badd6322893fa122302577c82b9617/setuptools-75.3.4.tar.gz" } ], "project_name": "setuptools", @@ -5395,7 +5570,7 @@ "wheel>=0.44.0; extra == \"test\"" ], "requires_python": ">=3.8", - "version": "75.3.2" + "version": "75.3.4" }, { "artifacts": [ @@ -5693,19 +5868,14 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "4ecca6dc0b9f963f8384e9d7fd529bf93dd7d708144c4fb5da0e0a1a926fee83", - "url": "https://files.pythonhosted.org/packages/49/58/97655efdfeb5b4eeab85b1fc5d3fa1023661246c2ab2a26ea8e47402d4f2/sseclient_py-1.8.0-py2.py3-none-any.whl" - }, - { - "algorithm": "sha256", - "hash": "c547c5c1a7633230a38dc599a21a2dc638f9b5c297286b48b46b935c71fac3e8", - "url": "https://files.pythonhosted.org/packages/e8/ed/3df5ab8bb0c12f86c28d0cadb11ed1de44a92ed35ce7ff4fd5518a809325/sseclient-py-1.8.0.tar.gz" + "hash": "340062b1587fc2880892811e2ab5b176d98ef3eee98b3672ff3a3ba1e8ed0f6f", + "url": "https://files.pythonhosted.org/packages/4d/2e/59920f7d66b7f9932a3d83dd0ec53fab001be1e058bf582606fe414a5198/sseclient_py-1.9.0-py3-none-any.whl" } ], "project_name": "sseclient-py", "requires_dists": [], - "requires_python": null, - "version": "1.8.0" + "requires_python": ">=3.7", + "version": "1.9.0" }, { "artifacts": [ @@ -5744,7 +5914,7 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "ab97c2a3d4a0a520d7fbdaf1dee5d670de9e51c157d11ed45322c84d6daadc50", + "hash": "69fe7f8c09c828d4a351f74be08834cf3e7164591ed2e40dd5c3db6ab127c2e4", "url": "git+https://github.com/StackStorm/st2-auth-ldap.git@master" } ], @@ -5754,20 +5924,20 @@ "python-ldap<3.5.0,>=3.4.0" ], "requires_python": ">=3.8", - "version": "3.9.dev0" + "version": "3.10.dev0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "f445faec6e5f55437f379be08dd78aa2f1181ed9dbd04fe19bddfb6f4c2fe911", + "hash": "ce1bcd8b63fb864688f94f12b20299fdf9ba856b23740bc5954224bca39064a6", "url": "git+https://github.com/StackStorm/st2-rbac-backend.git@master" } ], "project_name": "st2-rbac-backend", "requires_dists": [], "requires_python": null, - "version": "3.9.dev0" + "version": "3.10.dev0" }, { "artifacts": [ @@ -5850,49 +6020,49 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", - "url": "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl" + "hash": "1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", + "url": "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", - "url": "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", + "url": "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", - "url": "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz" + "hash": "5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", + "url": "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", - "url": "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl" + "hash": "aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", + "url": "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz" }, { "algorithm": "sha256", - "hash": "883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", - "url": "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl" + "hash": "1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", + "url": "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", - "url": "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + "hash": "133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", + "url": "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", - "url": "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl" + "hash": "5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", + "url": "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", - "url": "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl" + "hash": "551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", + "url": "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" } ], "project_name": "tomli", "requires_dists": [], "requires_python": ">=3.8", - "version": "2.3.0" + "version": "2.4.0" }, { "artifacts": [ @@ -5960,19 +6130,19 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", - "url": "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl" + "hash": "06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", + "url": "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", - "url": "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz" + "hash": "de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", + "url": "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz" } ], "project_name": "tzdata", "requires_dists": [], "requires_python": ">=2", - "version": "2025.2" + "version": "2025.3" }, { "artifacts": [ @@ -6401,19 +6571,19 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", - "url": "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl" + "hash": "1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", + "url": "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", - "url": "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz" + "hash": "cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", + "url": "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz" } ], "project_name": "wcwidth", "requires_dists": [], - "requires_python": ">=3.6", - "version": "0.2.14" + "requires_python": ">=3.8", + "version": "0.6.0" }, { "artifacts": [ @@ -6822,6 +6992,152 @@ "requires_python": ">=3.8", "version": "3.20.2" }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", + "url": "https://files.pythonhosted.org/packages/fe/42/f8dbc2b9ad59e927940325a22d6d3931d630c3644dae7e2369ef5d9ba230/zope.event-5.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd", + "url": "https://files.pythonhosted.org/packages/46/c2/427f1867bb96555d1d34342f1dd97f8c420966ab564d58d18469a1db8736/zope.event-5.0.tar.gz" + } + ], + "project_name": "zope-event", + "requires_dists": [ + "Sphinx; extra == \"docs\"", + "setuptools", + "zope.testrunner; extra == \"test\"" + ], + "requires_python": ">=3.7", + "version": "5.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "2ad9913fd858274db8dd867012ebe544ef18d218f6f7d1e3c3e6d98000f14b75", + "url": "https://files.pythonhosted.org/packages/88/d4/4ba1569b856870527cec4bf22b91fe704b81a3c1a451b2ccf234e9e0666f/zope.interface-7.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "550f1c6588ecc368c9ce13c44a49b8d6b6f3ca7588873c679bd8fd88a1b557b6", + "url": "https://files.pythonhosted.org/packages/28/ea/fdd9813c1eafd333ad92464d57a4e3a82b37ae57c19497bcffa42df673e4/zope.interface-7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "52e446f9955195440e787596dccd1411f543743c359eeb26e9b2c02b077b0519", + "url": "https://files.pythonhosted.org/packages/2a/dd/fcd313ee216ad0739ae00e6126bc22a0af62a74f76a9ca668d16cd276222/zope.interface-7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "8b49f1a3d1ee4cdaf5b32d2e738362c7f5e40ac8b46dd7d1a65e82a4872728fe", + "url": "https://files.pythonhosted.org/packages/30/93/9210e7606be57a2dfc6277ac97dcc864fd8d39f142ca194fdc186d596fda/zope.interface-7.2.tar.gz" + }, + { + "algorithm": "sha256", + "hash": "72cd1790b48c16db85d51fbbd12d20949d7339ad84fd971427cf00d990c1f137", + "url": "https://files.pythonhosted.org/packages/36/22/b1abd91854c1be03f5542fe092e6a745096d2eca7704d69432e119100583/zope.interface-7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "0ef9e2f865721553c6f22a9ff97da0f0216c074bd02b25cf0d3af60ea4d6931d", + "url": "https://files.pythonhosted.org/packages/3b/d3/0000a4d497ef9fbf4f66bb6828b8d0a235e690d57c333be877bec763722f/zope.interface-7.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "27f926f0dcb058211a3bb3e0e501c69759613b17a553788b2caeb991bed3b61d", + "url": "https://files.pythonhosted.org/packages/3e/e5/0b359e99084f033d413419eff23ee9c2bd33bca2ca9f4e83d11856f22d10/zope.interface-7.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "033b3923b63474800b04cba480b70f6e6243a62208071fc148354f3f89cc01b7", + "url": "https://files.pythonhosted.org/packages/49/b4/451f19448772b4a1159519033a5f72672221e623b0a1bd2b896b653943d8/zope.interface-7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a", + "url": "https://files.pythonhosted.org/packages/52/db/7e5f4226bef540f6d55acfd95cd105782bc6ee044d9b5587ce2c95558a5e/zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "a102424e28c6b47c67923a1f337ede4a4c2bba3965b01cf707978a801fc7442c", + "url": "https://files.pythonhosted.org/packages/65/94/5aa4461c10718062c8f8711161faf3249d6d3679c24a0b81dd6fc8ba1dd3/zope.interface-7.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2", + "url": "https://files.pythonhosted.org/packages/76/71/e6177f390e8daa7e75378505c5ab974e0bf59c1d3b19155638c7afbf4b2d/zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "baf95683cde5bc7d0e12d8e7588a3eb754d7c4fa714548adcd96bdf90169f021", + "url": "https://files.pythonhosted.org/packages/8a/cd/f1e8303b81151cd6ba6e229db5fe601f9867a7b29f24a95eeba255970099/zope.interface-7.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "7bd449c306ba006c65799ea7912adbbfed071089461a19091a228998b82b1fdb", + "url": "https://files.pythonhosted.org/packages/8c/2c/1f49dc8b4843c4f0848d8e43191aed312bad946a1563d1bf9e46cf2816ee/zope.interface-7.2-cp39-cp39-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "1909f52a00c8c3dcab6c4fad5d13de2285a4b3c7be063b239b8dc15ddfb73bd2", + "url": "https://files.pythonhosted.org/packages/98/7d/2e8daf0abea7798d16a58f2f3a2bf7588872eee54ac119f99393fdd47b65/zope.interface-7.2-cp311-cp311-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "224b7b0314f919e751f2bca17d15aad00ddbb1eadf1cb0190fa8175edb7ede62", + "url": "https://files.pythonhosted.org/packages/9e/dd/5505c6fa2dd3a6b76176c07bc85ad0c24f218a3e7c929272384a5eb5f18a/zope.interface-7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "25e6a61dcb184453bb00eafa733169ab6d903e46f5c2ace4ad275386f9ab327a", + "url": "https://files.pythonhosted.org/packages/9f/aa/1a28c02815fe1ca282b54f6705b9ddba20328fabdc37b8cf73fc06b172f0/zope.interface-7.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "80ecf2451596f19fd607bb09953f426588fc1e79e93f5968ecf3367550396b22", + "url": "https://files.pythonhosted.org/packages/a0/2a/0c03c7170fe61d0d371e4c7ea5b62b8cb79b095b3d630ca16719bf8b7b18/zope.interface-7.2-cp311-cp311-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "d3a8ffec2a50d8ec470143ea3d15c0c52d73df882eef92de7537e8ce13475e8a", + "url": "https://files.pythonhosted.org/packages/e6/0f/4e296d4b36ceb5464b671443ac4084d586d47698610025c4731ff2d30eae/zope.interface-7.2-cp38-cp38-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "a19a6cc9c6ce4b1e7e3d319a473cf0ee989cbbe2b39201d7c19e214d2dfb80c7", + "url": "https://files.pythonhosted.org/packages/ed/7d/83ddbfc8424c69579a90fc8edc2b797223da2a8083a94d8dfa0e374c5ed4/zope.interface-7.2-cp39-cp39-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "31d06db13a30303c08d61d5fb32154be51dfcbdb8438d2374ae27b4e069aac40", + "url": "https://files.pythonhosted.org/packages/f5/8c/49548aaa4f691615d703b5bee88ea67f68eac8f94c9fb6f1b2f4ae631354/zope.interface-7.2-cp38-cp38-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "e204937f67b28d2dca73ca936d3039a144a081fc47a07598d44854ea2a106239", + "url": "https://files.pythonhosted.org/packages/fe/f9/5a66f98f8c21d644d94f95b9484564f76e175034de3a57a45ba72238ce10/zope.interface-7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + } + ], + "project_name": "zope-interface", + "requires_dists": [ + "Sphinx; extra == \"docs\"", + "coverage[toml]; extra == \"test\"", + "coverage[toml]; extra == \"testing\"", + "furo; extra == \"docs\"", + "repoze.sphinx.autointerface; extra == \"docs\"", + "setuptools", + "zope.event; extra == \"test\"", + "zope.event; extra == \"testing\"", + "zope.testing; extra == \"test\"", + "zope.testing; extra == \"testing\"" + ], + "requires_python": ">=3.8", + "version": "7.2" + }, { "artifacts": [ { @@ -7143,6 +7459,7 @@ "eventlet", "flask", "flex", + "gevent", "gitdb", "gitpython", "graphviz", diff --git a/requirements-pants.txt b/requirements-pants.txt index 87f4bebdb4..6c806d754a 100644 --- a/requirements-pants.txt +++ b/requirements-pants.txt @@ -18,7 +18,8 @@ flex # gitpython & gitdb are used for pack management gitdb gitpython -# st2common/tests/integration/test_util_green.py requires greenlet (as does eventlet) +# st2common/tests/integration/test_util_green.py requires greenlet (as does eventlet and gevent) +gevent greenlet gunicorn jinja2 From f8c1a79bf383e5717acade169e61484f8c1bf11b Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 19 Feb 2026 23:17:38 +0100 Subject: [PATCH 04/22] Move explicit eventlet calls behind concurrency module in auth. --- st2auth/st2auth/cmd/api.py | 9 +++--- st2common/st2common/util/concurrency.py | 43 +++++++++++++++++++------ 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/st2auth/st2auth/cmd/api.py b/st2auth/st2auth/cmd/api.py index e817765d97..7a379814e6 100644 --- a/st2auth/st2auth/cmd/api.py +++ b/st2auth/st2auth/cmd/api.py @@ -1,4 +1,4 @@ -# Copyright 2020 The StackStorm Authors. +# Copyright 2020-2026 The StackStorm Authors. # Copyright 2019 Extreme Networks, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,12 +17,11 @@ monkey_patch() -import eventlet +from st2common.util import concurrency import os import sys from oslo_config import cfg -from eventlet import wsgi from st2common import log as logging from st2common.service_setup import setup as common_setup @@ -81,7 +80,7 @@ def _run_server(): if use_ssl and not os.path.isfile(key_file_path): raise ValueError('Private key file "%s" doesn\'t exist' % (key_file_path)) - socket = eventlet.listen((host, port)) + socket = concurrency.listen(host, port) if use_ssl: socket = eventlet.wrap_ssl( @@ -96,7 +95,7 @@ def _run_server(): host, port, ) - + wsgi = concurrency.get_wsgi_module() wsgi.server(socket, app.setup_app(), log=LOG, log_output=False) return 0 diff --git a/st2common/st2common/util/concurrency.py b/st2common/st2common/util/concurrency.py index f064be4292..00b3e89061 100644 --- a/st2common/st2common/util/concurrency.py +++ b/st2common/st2common/util/concurrency.py @@ -73,6 +73,21 @@ def get_subprocess_module(): from gevent import subprocess # pylint: disable=import-error return subprocess + else: + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + + +def get_wsgi_module(): + if CONCURRENCY_LIBRARY == "eventlet": + from eventlet import wsgi + + return wsgi + elif CONCURRENCY_LIBRARY == "gevent": + from gevent import pywsgi + + return pywsgi + else: + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") def subprocess_popen(*args, **kwargs): @@ -84,6 +99,8 @@ def subprocess_popen(*args, **kwargs): from gevent import subprocess # pylint: disable=import-error return subprocess.Popen(*args, **kwargs) + else: + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") def spawn(func, *args, **kwargs): @@ -92,7 +109,7 @@ def spawn(func, *args, **kwargs): elif CONCURRENCY_LIBRARY == "gevent": return gevent.spawn(func, *args, **kwargs) else: - raise ValueError("Unsupported concurrency library") + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") def wait(green_thread, *args, **kwargs): @@ -101,7 +118,7 @@ def wait(green_thread, *args, **kwargs): elif CONCURRENCY_LIBRARY == "gevent": return green_thread.join(*args, **kwargs) else: - raise ValueError("Unsupported concurrency library") + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") def cancel(green_thread, *args, **kwargs): @@ -110,7 +127,7 @@ def cancel(green_thread, *args, **kwargs): elif CONCURRENCY_LIBRARY == "gevent": return green_thread.kill(*args, **kwargs) else: - raise ValueError("Unsupported concurrency library") + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") def kill(green_thread, *args, **kwargs): @@ -119,7 +136,16 @@ def kill(green_thread, *args, **kwargs): elif CONCURRENCY_LIBRARY == "gevent": return green_thread.kill(*args, **kwargs) else: - raise ValueError("Unsupported concurrency library") + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + + +def listen(host, port): + if CONCURRENCY_LIBRARY == "eventlet": + return eventlet.listen((host, port)) + elif CONCURRENCY_LIBRARY == "gevent": + raise NotImplementedError + else: + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") def sleep(*args, **kwargs): @@ -128,7 +154,7 @@ def sleep(*args, **kwargs): elif CONCURRENCY_LIBRARY == "gevent": return gevent.sleep(*args, **kwargs) else: - raise ValueError("Unsupported concurrency library") + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") def get_greenlet_exit_exception_class(): @@ -137,7 +163,7 @@ def get_greenlet_exit_exception_class(): elif CONCURRENCY_LIBRARY == "gevent": return gevent.GreenletExit else: - raise ValueError("Unsupported concurrency library") + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") def get_default_green_pool_size(): @@ -156,7 +182,7 @@ def get_green_pool_class(): elif CONCURRENCY_LIBRARY == "gevent": return gevent.pool.Pool else: - raise ValueError("Unsupported concurrency library") + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") def is_green_pool_free(pool): @@ -168,7 +194,7 @@ def is_green_pool_free(pool): elif CONCURRENCY_LIBRARY == "gevent": return not pool.full() else: - raise ValueError("Unsupported concurrency library") + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") def green_pool_wait_all(pool): @@ -194,4 +220,3 @@ def listen_server(host, port): return sock.listen(5) else: raise ValueError("Unsupported concurrency library") - From c07cc1f55bc74fe61bebeb7dd8ca8b0022ede63b Mon Sep 17 00:00:00 2001 From: Carlos Date: Sun, 1 Mar 2026 16:04:25 +0100 Subject: [PATCH 05/22] Move blocking detection to concurrency module + black format --- st2common/st2common/service_setup.py | 33 +++++++++---------------- st2common/st2common/util/concurrency.py | 30 +++++++++++++++++++--- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/st2common/st2common/service_setup.py b/st2common/st2common/service_setup.py index 9e8ae7e07f..3e91ca7aee 100644 --- a/st2common/st2common/service_setup.py +++ b/st2common/st2common/service_setup.py @@ -1,4 +1,4 @@ -# Copyright 2020 The StackStorm Authors. +# Copyright 2020-2026 The StackStorm Authors. # Copyright 2019 Extreme Networks, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,7 +26,7 @@ import logging as stdlib_logging import six -import eventlet.debug + from oslo_config import cfg from tooz.coordination import GroupAlreadyExist from tooz.coordination import GroupNotCreated @@ -38,6 +38,7 @@ from st2common.transport.bootstrap_utils import register_kombu_serializers from st2common.bootstrap import runnersregistrar from st2common.signal_handlers import register_common_signal_handlers +from st2common.util.concurrency import blocking_detection from st2common.util.debugging import enable_debugging from st2common.models.utils.profiling import enable_profiling from st2common import triggers @@ -182,8 +183,7 @@ def setup( or encoding.lower() not in VALID_UTF8_ENCODINGS ): LOG.warning( - NON_UTF8_LOCALE_WARNING_MSG - % (fs_encoding, default_encoding, used_locale.strip()) + NON_UTF8_LOCALE_WARNING_MSG % (fs_encoding, default_encoding, used_locale.strip()) ) is_debug_enabled = cfg.CONF.debug or cfg.CONF.system.debug @@ -197,9 +197,7 @@ def setup( except KeyError as e: tb_msg = traceback.format_exc() if "log.setLevel" in tb_msg: - msg = ( - "Invalid log level selected. Log level names need to be all uppercase." - ) + msg = "Invalid log level selected. Log level names need to be all uppercase." msg += "\n\n" + getattr(e, "message", six.text_type(e)) raise KeyError(msg) else: @@ -214,8 +212,7 @@ def setup( # set to "INFO" and we already log messages with level AUDIT to a special dedicated log # file. ignore_audit_log_messages = ( - handler.level >= stdlib_logging.INFO - and handler.level < stdlib_logging.AUDIT + handler.level >= stdlib_logging.INFO and handler.level < stdlib_logging.AUDIT ) if not is_debug_enabled and ignore_audit_log_messages: try: @@ -224,10 +221,7 @@ def setup( # In case handler doesn't have name assigned, repr would throw handler_repr = "unknown" - LOG.debug( - 'Excluding log messages with level "AUDIT" for handler "%s"' - % (handler_repr) - ) + LOG.debug('Excluding log messages with level "AUDIT" for handler "%s"' % (handler_repr)) handler.addFilter(LogLevelFilter(log_levels=exclude_log_levels)) if not is_debug_enabled: @@ -285,11 +279,10 @@ def setup( # modules like jinja, stevedore, etc load files from disk on init which is slow and will be # detected as blocking operation, but this is not really an issue inside the service startup / # init phase. - if cfg.CONF.enable_eventlet_blocking_detection: - print("Eventlet long running / blocking operation detection logic enabled") - print(cfg.CONF.eventlet_blocking_detection_resolution) - eventlet.debug.hub_blocking_detection( - state=True, resolution=cfg.CONF.eventlet_blocking_detection_resolution + if cfg.CONF.enable_concurrency_blocking_detection: + blocking_detection( + cfg.CONF.enable_concurrency_blocking_detection, + cfg.CONF.concurrency_blocking_detection_resolution, ) @@ -354,9 +347,7 @@ def deregister_service(service, start_heart=True): coordinator = coordination.get_coordinator(start_heart=start_heart) member_id = coordination.get_member_id() - LOG.debug( - 'Leaving service registry group "%s" as member_id "%s"' % (group_id, member_id) - ) + LOG.debug('Leaving service registry group "%s" as member_id "%s"' % (group_id, member_id)) try: coordinator.leave_group(group_id).get() except (GroupNotCreated, MemberNotJoined): diff --git a/st2common/st2common/util/concurrency.py b/st2common/st2common/util/concurrency.py index 00b3e89061..9a73e2cac9 100644 --- a/st2common/st2common/util/concurrency.py +++ b/st2common/st2common/util/concurrency.py @@ -171,7 +171,7 @@ def get_default_green_pool_size(): return eventlet.wsgi.DEFAULT_MAX_SIMULTANEOUS_REQUESTS elif CONCURRENCY_LIBRARY == "gevent": # matches what DEFAULT_MAX_SIMULTANEOUS_REQUESTS is for eventlet - return 1024 + return 1024 else: raise ValueError("Unsupported concurrency library") @@ -210,13 +210,35 @@ def green_pool_wait_all(pool): else: raise ValueError("Unsupported concurrency library") -def listen_server(host, port): + +def listen_server(host, port, backlog=50, **kwargs): + """ + Start listening on the host:port. + :backlog: the number of unaccepted connections that the system will allow before refusing new connections. + """ if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.listen((host, port)) + return eventlet.listen((host, port), backlog=backlog, **kwargs) elif CONCURRENCY_LIBRARY == "gevent": import socket + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind((host, port)) - return sock.listen(5) + return sock.listen(backlog) + else: + raise ValueError("Unsupported concurrency library") + + +def blocking_detection(enable=False, timeout=1.0): + if CONCURRENCY_LIBRARY == "eventlet": + print( + f"Eventlet long running / blocking operation detection logic enabled. Block timeout ({timeout})." + ) + eventlet.debug.hub_blocking_detection(enable_detection=enable, resolution=timeout) + elif CONCURRENCY_LIBRARY == "gevent": + print( + f"gEvent long running / blocking operation detection logic enabled. Block timeout ({timeout})." + ) + gevent.config.monitor_thread = enable + gevent.config.max_blocking_time = timeout else: raise ValueError("Unsupported concurrency library") From cbf2e2df228f9abddfd364bc2c54a7126201252f Mon Sep 17 00:00:00 2001 From: Carlos Date: Fri, 6 Mar 2026 18:19:54 +0100 Subject: [PATCH 06/22] Move wsgi behind concurrency library. --- st2api/st2api/cmd/api.py | 20 +++++++++---------- st2auth/st2auth/cmd/api.py | 26 +++++++++++++------------ st2common/st2common/config.py | 12 ++++++------ st2common/st2common/util/concurrency.py | 23 +++++++++++++++++----- st2stream/st2stream/cmd/api.py | 24 ++++++----------------- 5 files changed, 53 insertions(+), 52 deletions(-) diff --git a/st2api/st2api/cmd/api.py b/st2api/st2api/cmd/api.py index b80f6f4bed..04bf85b526 100644 --- a/st2api/st2api/cmd/api.py +++ b/st2api/st2api/cmd/api.py @@ -1,4 +1,4 @@ -# Copyright 2020 The StackStorm Authors. +# Copyright 2020-2026 The StackStorm Authors. # Copyright 2019 Extreme Networks, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,20 +13,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os -import sys - # NOTE: It's important that we perform monkey patch as early as possible before any other modules # are important, otherwise SSL support for MongoDB won't work. # See https://github.com/StackStorm/st2/issues/4832 and https://github.com/gevent/gevent/issues/1016 # for details. from st2common.util.monkey_patch import monkey_patch - monkey_patch() -import eventlet +import os +import sys + from oslo_config import cfg -from eventlet import wsgi +from st2common.util import concurrency from st2common import log as logging from st2common.service_setup import setup as common_setup @@ -79,11 +77,11 @@ def _run_server(): LOG.info("(PID=%s) ST2 API is serving on http://%s:%s.", os.getpid(), host, port) - max_pool_size = eventlet.wsgi.DEFAULT_MAX_SIMULTANEOUS_REQUESTS - worker_pool = eventlet.GreenPool(max_pool_size) - sock = eventlet.listen((host, port)) + max_pool_size = concurrency.get_default_green_pool_size() + worker_pool = concurrency.get_green_pool_class()(max_pool_size) + sock = concurrency.listen_server(host, port) - wsgi.server( + concurrency.wsgi_server( sock, app.setup_app(), custom_pool=worker_pool, log=LOG, log_output=False ) return 0 diff --git a/st2auth/st2auth/cmd/api.py b/st2auth/st2auth/cmd/api.py index 7a379814e6..125ec6c305 100644 --- a/st2auth/st2auth/cmd/api.py +++ b/st2auth/st2auth/cmd/api.py @@ -14,10 +14,8 @@ # limitations under the License. from st2common.util.monkey_patch import monkey_patch - monkey_patch() -from st2common.util import concurrency import os import sys @@ -27,6 +25,7 @@ from st2common.service_setup import setup as common_setup from st2common.service_setup import teardown as common_teardown from st2common.service_setup import deregister_service +from st2common.util import concurrency from st2auth import config config.register_opts(ignore_errors=True) @@ -74,29 +73,32 @@ def _run_server(): cert_file_path = os.path.realpath(cfg.CONF.auth.cert) key_file_path = os.path.realpath(cfg.CONF.auth.key) - if use_ssl and not os.path.isfile(cert_file_path): - raise ValueError('Certificate file "%s" doesn\'t exist' % (cert_file_path)) - - if use_ssl and not os.path.isfile(key_file_path): - raise ValueError('Private key file "%s" doesn\'t exist' % (key_file_path)) - socket = concurrency.listen(host, port) if use_ssl: - socket = eventlet.wrap_ssl( + scheme = "https" + if not os.path.isfile(cert_file_path): + raise ValueError(f'Certificate file "{cert_file_path}" does not exist') + + if not os.path.isfile(key_file_path): + raise ValueError(f'Private key file "{key_file_path}" does not exist') + + socket = concurrency.wrap_ssl( socket, certfile=cert_file_path, keyfile=key_file_path, server_side=True ) + else: + scheme = "http" LOG.info('ST2 Auth API running in "%s" auth mode', cfg.CONF.auth.mode) LOG.info( "(PID=%s) ST2 Auth API is serving on %s://%s:%s.", os.getpid(), - "https" if use_ssl else "http", + scheme, host, port, ) - wsgi = concurrency.get_wsgi_module() - wsgi.server(socket, app.setup_app(), log=LOG, log_output=False) + + concurrency.wsgi_server(socket, app.setup_app(), log=LOG, log_output=False) return 0 diff --git a/st2common/st2common/config.py b/st2common/st2common/config.py index a9461951d1..53b5f737c1 100644 --- a/st2common/st2common/config.py +++ b/st2common/st2common/config.py @@ -759,8 +759,8 @@ def register_opts(ignore_errors=False): cfg.BoolOpt( "use-debugger", default=True, - help="Enables debugger. Note that using this option changes how the " - "eventlet library is used to support async IO. This could result in " + help="Enables debugger. Note that using this option changes how the " + "concurrency library is used to support async IO. This could result in " "failures that do not occur under normal operation.", ), cfg.BoolOpt( @@ -769,14 +769,14 @@ def register_opts(ignore_errors=False): help="Enable code profiler mode. Do not use in production.", ), cfg.BoolOpt( - "enable-eventlet-blocking-detection", + "enable-concurrency-blocking-detection", default=False, - help="Enable eventlet blocking detection logic. Do not use in production.", + help="Enable blocking detection logic in concurrency library. Do not use in production.", ), cfg.FloatOpt( - "eventlet-blocking-detection-resolution", + "concurrency-blocking-detection-resolution", default=0.5, - help="Resolution in seconds for eventlet blocking detection logic.", + help="Resolution in seconds for blocking detection logic in currency library.", ), ] diff --git a/st2common/st2common/util/concurrency.py b/st2common/st2common/util/concurrency.py index 9a73e2cac9..edad8770e1 100644 --- a/st2common/st2common/util/concurrency.py +++ b/st2common/st2common/util/concurrency.py @@ -76,20 +76,22 @@ def get_subprocess_module(): else: raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") - -def get_wsgi_module(): +# socket, app.setup_app(), log=LOG, log_output=False +# sock, app.setup_app(), custom_pool=worker_pool, log=LOG, log_output=False +# sock, app.setup_app(), custom_pool=worker_pool +def wsgi_server(socket, app, custom_pool=None, log=None, log_output=True, *args, **kwargs): if CONCURRENCY_LIBRARY == "eventlet": from eventlet import wsgi - return wsgi + wsgi.server(socket, app, custom_pool=custom_pool, log=log, log_output=log_output, *args, **kwargs) elif CONCURRENCY_LIBRARY == "gevent": from gevent import pywsgi - return pywsgi + # Figure out how to handle custom pool + pywsgi.WSGIServer(socket, app) else: raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") - def subprocess_popen(*args, **kwargs): if CONCURRENCY_LIBRARY == "eventlet": from eventlet.green import subprocess # pylint: disable=import-error @@ -227,6 +229,17 @@ def listen_server(host, port, backlog=50, **kwargs): else: raise ValueError("Unsupported concurrency library") +def wrap_ssl(socket, *args, **kwargs): + if CONCURRENCY_LIBRARY == "eventlet": + return eventlet.wrap_socket(socket, *args, **kwargs) + elif CONCURRENCY_LIBRARY == "gevent": + # Monkey patching in the caller module is required prior to + # calling wrap_ssl() or this may block. + import ssl + + return ssl.wrap_socket(socket, *args, **kwargs) + else: + raise ValueError("Unsupported concurrency library") def blocking_detection(enable=False, timeout=1.0): if CONCURRENCY_LIBRARY == "eventlet": diff --git a/st2stream/st2stream/cmd/api.py b/st2stream/st2stream/cmd/api.py index 08caa0a2bd..3b4b393695 100644 --- a/st2stream/st2stream/cmd/api.py +++ b/st2stream/st2stream/cmd/api.py @@ -1,4 +1,4 @@ -# Copyright 2020 The StackStorm Authors. +# Copyright 2020-2026 The StackStorm Authors. # Copyright 2019 Extreme Networks, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,16 +14,12 @@ # limitations under the License. from st2common.util.monkey_patch import monkey_patch - monkey_patch() import os import sys -import eventlet - from oslo_config import cfg -from eventlet import wsgi from st2common import log as logging from st2common.service_setup import setup as common_setup @@ -42,14 +38,6 @@ __all__ = ["main"] -#eventlet.monkey_patch( -# os=True, -# select=True, -# socket=True, -# thread=False if "--use-debugger" in sys.argv else True, -# time=True, -#) - LOG = logging.getLogger(__name__) STREAM = "stream" @@ -76,8 +64,8 @@ def _setup(): capabilities=capabilities, ) - def _run_server(): + host = cfg.CONF.stream.host port = cfg.CONF.stream.port @@ -85,13 +73,13 @@ def _run_server(): "(PID=%s) ST2 Stream API is serving on http://%s:%s.", os.getpid(), host, port ) - max_pool_size = concurrency.get_default_green_pool_size() + max_pool_size = concurrency.get_default_green_pool_size() worker_pool = concurrency.get_green_pool_class()(max_pool_size) - sock = concurrency.listen_server(host, port) + sock = concurrency.listen_server(host, port) def queue_shutdown(signal_number, stack_frame): deregister_service(STREAM) - eventlet.spawn_n( + concurrent.spawn( shutdown_server_kill_pending_requests, sock=sock, worker_pool=worker_pool, @@ -103,7 +91,7 @@ def queue_shutdown(signal_number, stack_frame): # will still want to kill long running stream requests. register_stream_signal_handlers(handler_func=queue_shutdown) - wsgi.server(sock, app.setup_app(), custom_pool=worker_pool) + concurrency.wsgi_server(sock, app.setup_app(), custom_pool=worker_pool) return 0 From 505287537e2383ebd6af306536d76c47e52ae7d1 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Thu, 26 Mar 2026 15:15:46 -0600 Subject: [PATCH 07/22] more helpers in concurrency, migrate all files to use concurrency, update tests --- .../action_chain_runner.py | 6 +- .../tests/unit/test_actionchain_cancel.py | 5 +- .../unit/test_actionchain_notifications.py | 5 +- .../unit/test_actionchain_pause_resume.py | 7 +- st2actions/st2actions/policies/retry.py | 7 +- st2actions/st2actions/scheduler/handler.py | 12 +-- st2actions/st2actions/workflows/workflows.py | 6 +- .../test_actions_queue_consumer.py | 11 +-- st2actions/tests/unit/test_scheduler.py | 5 +- .../tests/unit/test_scheduler_entrypoint.py | 27 +++--- st2actions/tests/unit/test_scheduler_retry.py | 15 ++-- st2actions/tests/unit/test_worker.py | 35 ++++---- st2actions/tests/unit/test_workflow_engine.py | 33 +++---- .../integration/test_gunicorn_configs.py | 10 ++- st2client/st2client/utils/httpclient.py | 2 +- st2common/st2common/stream/listener.py | 10 +-- st2common/st2common/util/concurrency.py | 89 ++++++++++++++++--- st2common/st2common/util/greenpooldispatch.py | 19 ++-- st2common/st2common/util/wsgi.py | 14 +-- st2common/tests/integration/test_logging.py | 17 ++-- .../test_service_setup_log_level_filtering.py | 23 ++--- .../tests/unit/test_greenpooldispatch.py | 6 +- st2common/tests/unit/test_stream_generator.py | 2 +- st2common/tests/unit/test_transport.py | 13 +-- st2reactor/tests/unit/test_sensor_wrapper.py | 11 ++- st2stream/st2stream/cmd/api.py | 2 +- .../v1/test_stream_execution_output.py | 10 +-- st2tests/st2tests/base.py | 26 +++--- st2tests/st2tests/mocks/execution.py | 5 +- st2tests/st2tests/mocks/liveaction.py | 11 +-- st2tests/st2tests/mocks/workflow.py | 9 +- 31 files changed, 268 insertions(+), 185 deletions(-) diff --git a/contrib/runners/action_chain_runner/action_chain_runner/action_chain_runner.py b/contrib/runners/action_chain_runner/action_chain_runner/action_chain_runner.py index 57c015adcf..82af625004 100644 --- a/contrib/runners/action_chain_runner/action_chain_runner/action_chain_runner.py +++ b/contrib/runners/action_chain_runner/action_chain_runner/action_chain_runner.py @@ -15,7 +15,6 @@ from __future__ import absolute_import -import eventlet import traceback import uuid import datetime @@ -24,6 +23,7 @@ from jsonschema import exceptions as json_schema_exc from oslo_config import cfg +from st2common.util import concurrency from st2common.runners.base import ActionRunner from st2common.runners.base import get_metadata as get_runner_metadata from st2common import log as logging @@ -879,7 +879,7 @@ def _run_action(self, liveaction, wait_for_completion=True, sleep_delay=1.0): action_constants.LIVEACTION_STATUS_PENDING, ] ): - eventlet.sleep(sleep_delay) + concurrency.sleep(sleep_delay) liveaction = action_db_util.get_liveaction_by_id(liveaction.id) return liveaction @@ -901,7 +901,7 @@ def _resume_action(self, liveaction, wait_for_completion=True, sleep_delay=1.0): action_constants.LIVEACTION_COMPLETED_STATES + [action_constants.LIVEACTION_STATUS_PAUSED] ): - eventlet.sleep(sleep_delay) + concurrency.sleep(sleep_delay) liveaction = action_db_util.get_liveaction_by_id(liveaction.id) return liveaction diff --git a/contrib/runners/action_chain_runner/tests/unit/test_actionchain_cancel.py b/contrib/runners/action_chain_runner/tests/unit/test_actionchain_cancel.py index bf6ffdd47a..6c4e3b8397 100644 --- a/contrib/runners/action_chain_runner/tests/unit/test_actionchain_cancel.py +++ b/contrib/runners/action_chain_runner/tests/unit/test_actionchain_cancel.py @@ -14,8 +14,9 @@ # limitations under the License. from __future__ import absolute_import -import eventlet import mock + +from st2common.util import concurrency import os import tempfile @@ -98,7 +99,7 @@ def _wait_for_children(self, execution, interval=0.1, retries=100): for i in range(0, retries): execution = ActionExecution.get_by_id(str(execution.id)) if len(getattr(execution, "children", [])) <= 0: - eventlet.sleep(interval) + concurrency.sleep(interval) continue return execution diff --git a/contrib/runners/action_chain_runner/tests/unit/test_actionchain_notifications.py b/contrib/runners/action_chain_runner/tests/unit/test_actionchain_notifications.py index f2f2a680c8..82cd10cbe6 100644 --- a/contrib/runners/action_chain_runner/tests/unit/test_actionchain_notifications.py +++ b/contrib/runners/action_chain_runner/tests/unit/test_actionchain_notifications.py @@ -14,9 +14,10 @@ # limitations under the License. from __future__ import absolute_import -import eventlet import mock +from st2common.util import concurrency + from st2common.bootstrap import actionsregistrar from st2common.bootstrap import runnersregistrar from st2common.constants import action as action_constants @@ -221,7 +222,7 @@ def _wait_for_children( % (expected_children, found_children) ) - eventlet.sleep(interval) + concurrency.sleep(interval) found_children = len(getattr(execution, "children", [])) diff --git a/contrib/runners/action_chain_runner/tests/unit/test_actionchain_pause_resume.py b/contrib/runners/action_chain_runner/tests/unit/test_actionchain_pause_resume.py index e0dfecc4d6..fa8a0c2245 100644 --- a/contrib/runners/action_chain_runner/tests/unit/test_actionchain_pause_resume.py +++ b/contrib/runners/action_chain_runner/tests/unit/test_actionchain_pause_resume.py @@ -14,8 +14,9 @@ # limitations under the License. from __future__ import absolute_import -import eventlet import mock + +from st2common.util import concurrency import os import tempfile @@ -120,7 +121,7 @@ def _wait_for_status(self, liveaction, status, interval=0.1, retries=100): for i in range(0, retries): liveaction = LiveAction.get_by_id(str(liveaction.id)) if liveaction.status != status: - eventlet.sleep(interval) + concurrency.sleep(interval) continue else: break @@ -132,7 +133,7 @@ def _wait_for_children(self, execution, interval=0.1, retries=100): for i in range(0, retries): execution = ActionExecution.get_by_id(str(execution.id)) if len(getattr(execution, "children", [])) <= 0: - eventlet.sleep(interval) + concurrency.sleep(interval) continue return execution diff --git a/st2actions/st2actions/policies/retry.py b/st2actions/st2actions/policies/retry.py index 225952875d..6fc546a738 100644 --- a/st2actions/st2actions/policies/retry.py +++ b/st2actions/st2actions/policies/retry.py @@ -17,9 +17,8 @@ import functools -import eventlet - from st2common import log as logging +from st2common.util import concurrency from st2common.models.db.liveaction import LiveActionDB import st2common.services.action as action_services from st2common.constants.action import LIVEACTION_STATUS_FAILED @@ -103,7 +102,7 @@ def apply_after(self, target): # status if self.delay > 0: re_run_live_action = functools.partial( - eventlet.spawn_after, + concurrency.spawn_after, self.delay, self._re_run_live_action, live_action_db=live_action_db, @@ -111,7 +110,7 @@ def apply_after(self, target): else: # Even if delay is 0, use a small delay (0.1 seconds) to prevent busy wait re_run_live_action = functools.partial( - eventlet.spawn_after, + concurrency.spawn_after, 0.1, self._re_run_live_action, live_action_db=live_action_db, diff --git a/st2actions/st2actions/scheduler/handler.py b/st2actions/st2actions/scheduler/handler.py index b6eb994f72..636d0249da 100644 --- a/st2actions/st2actions/scheduler/handler.py +++ b/st2actions/st2actions/scheduler/handler.py @@ -15,7 +15,6 @@ from __future__ import absolute_import -import eventlet import retrying from oslo_config import cfg @@ -34,6 +33,7 @@ from st2common.persistence.liveaction import LiveAction from st2common.persistence.execution_queue import ActionExecutionSchedulingQueue from st2common.util import action_db as action_utils +from st2common.util import concurrency from st2common.metrics import base as metrics from st2common.exceptions import db as db_exc @@ -51,7 +51,7 @@ class ActionExecutionSchedulingQueueHandler(object): def __init__(self): self.message_type = LiveActionDB self._shutdown = False - self._pool = eventlet.GreenPool(size=cfg.CONF.scheduler.pool_size) + self._pool = concurrency.get_green_pool_class()(size=cfg.CONF.scheduler.pool_size) # If an ActionExecutionSchedulingQueueItemDB object hasn't been updated fore more than # this amount of milliseconds, it will be marked as "handled=False". # As soon as an item is picked by scheduler to be processed, it should be processed very @@ -69,7 +69,7 @@ def run(self): LOG.debug("Starting scheduler handler...") while not self._shutdown: - eventlet.greenthread.sleep(cfg.CONF.scheduler.sleep_interval) + concurrency.sleep(cfg.CONF.scheduler.sleep_interval) self.process() @retrying.retry( @@ -87,7 +87,7 @@ def cleanup(self): LOG.debug("Starting scheduler garbage collection...") while not self._shutdown: - eventlet.greenthread.sleep(cfg.CONF.scheduler.gc_interval) + concurrency.sleep(cfg.CONF.scheduler.gc_interval) self._handle_garbage_collection() def _reset_handling_flag(self): @@ -496,8 +496,8 @@ def start(self): self._shutdown = False # Spawn the worker threads. - self._main_thread = eventlet.spawn(self.run) - self._cleanup_thread = eventlet.spawn(self.cleanup) + self._main_thread = concurrency.spawn(self.run) + self._cleanup_thread = concurrency.spawn(self.cleanup) # Link the threads to the shutdown function. If either of the threads exited with error, # then initiate shutdown which will allow the waits below to throw exception to the diff --git a/st2actions/st2actions/workflows/workflows.py b/st2actions/st2actions/workflows/workflows.py index 700244d058..b8721e39fa 100644 --- a/st2actions/st2actions/workflows/workflows.py +++ b/st2actions/st2actions/workflows/workflows.py @@ -19,8 +19,6 @@ from orquesta import statuses from tooz.coordination import GroupNotCreated from st2common.services import coordination -from eventlet.semaphore import Semaphore -from eventlet import spawn_after from st2common.constants import action as ac_const from st2common import log as logging from st2common.metrics import base as metrics @@ -55,7 +53,7 @@ class WorkflowExecutionHandler(consumers.VariableMessageHandler): def __init__(self, connection, queues): super(WorkflowExecutionHandler, self).__init__(connection, queues) self._active_messages = 0 - self._semaphore = Semaphore() + self._semaphore = concurrency.Semaphore() # This is required to ensure workflows stuck in pausing state after shutdown transition to paused state after engine startup. self._delay = 30 @@ -106,7 +104,7 @@ def process(self, message): self._active_messages -= 1 def start(self, wait): - spawn_after(self._delay, self._resume_workflows_paused_during_shutdown) + concurrency.spawn_after(self._delay, self._resume_workflows_paused_during_shutdown) super(WorkflowExecutionHandler, self).start(wait=wait) def shutdown(self): diff --git a/st2actions/tests/integration/test_actions_queue_consumer.py b/st2actions/tests/integration/test_actions_queue_consumer.py index aa37b5e589..f03b935d29 100644 --- a/st2actions/tests/integration/test_actions_queue_consumer.py +++ b/st2actions/tests/integration/test_actions_queue_consumer.py @@ -15,10 +15,11 @@ from __future__ import absolute_import import random -import eventlet from unittest import TestCase +from st2common.util import concurrency + from st2common.transport.consumers import ActionsQueueConsumer from st2common.transport.kombu import Exchange, Queue from st2common.transport.publishers import PoolPublisher @@ -44,21 +45,21 @@ def test_stop_consumption_on_shutdown(self): watcher = ActionsQueueConsumer( connection=connection, queues=queue, handler=self ) - watcher_thread = eventlet.greenthread.spawn(watcher.run) + watcher_thread = concurrency.spawn(watcher.run) # Give it some time to start up since we are publishing on a new queue - eventlet.sleep(0.5) + concurrency.sleep(0.5) body = LiveActionDB( status="scheduled", action="core.local", action_is_workflow=False ) publisher.publish(payload=body, exchange=exchange) - eventlet.sleep(0.2) + concurrency.sleep(0.2) self.assertEqual(self.message_count, 1) body = LiveActionDB( status="scheduled", action="core.local", action_is_workflow=True ) watcher.shutdown() - eventlet.sleep(1) + concurrency.sleep(1) publisher.publish(payload=body, exchange=exchange) # Second published message won't be consumed. self.assertEqual(self.message_count, 1) diff --git a/st2actions/tests/unit/test_scheduler.py b/st2actions/tests/unit/test_scheduler.py index 65a59bd869..2b9c00a2fe 100644 --- a/st2actions/tests/unit/test_scheduler.py +++ b/st2actions/tests/unit/test_scheduler.py @@ -17,7 +17,8 @@ import datetime import mock -import eventlet + +from st2common.util import concurrency import st2common from st2tests import ExecutionDbTestCase @@ -147,7 +148,7 @@ def test_next_execution(self): ) # Wait maximum delay seconds so the query works as expected - eventlet.sleep(1.0) + concurrency.sleep(1.0) for index in expected_order: test_case = test_cases[index] diff --git a/st2actions/tests/unit/test_scheduler_entrypoint.py b/st2actions/tests/unit/test_scheduler_entrypoint.py index 2862ba2b3c..9c74718319 100644 --- a/st2actions/tests/unit/test_scheduler_entrypoint.py +++ b/st2actions/tests/unit/test_scheduler_entrypoint.py @@ -13,9 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import eventlet import mock +from st2common.util import concurrency + from st2actions.cmd.scheduler import _run_scheduler from st2actions.scheduler.handler import ActionExecutionSchedulingQueueHandler from st2actions.scheduler.entrypoint import SchedulerEntrypoint @@ -26,20 +27,20 @@ def mock_handler_run(self): - # NOTE: We use eventlet.sleep to emulate async nature of this process - eventlet.sleep(0.2) + # NOTE: We use concurrency.sleep to emulate async nature of this process + concurrency.sleep(0.2) raise Exception("handler run exception") def mock_handler_cleanup(self): - # NOTE: We use eventlet.sleep to emulate async nature of this process - eventlet.sleep(0.2) + # NOTE: We use concurrency.sleep to emulate async nature of this process + concurrency.sleep(0.2) raise Exception("handler clean exception") def mock_entrypoint_start(self): - # NOTE: We use eventlet.sleep to emulate async nature of this process - eventlet.sleep(0.2) + # NOTE: We use concurrency.sleep to emulate async nature of this process + concurrency.sleep(0.2) raise Exception("entrypoint start exception") @@ -47,8 +48,8 @@ class SchedulerServiceEntryPointTestCase(CleanDbTestCase): @mock.patch.object(ActionExecutionSchedulingQueueHandler, "run", mock_handler_run) @mock.patch("st2actions.cmd.scheduler.LOG") def test_service_exits_correctly_on_fatal_exception_in_handler_run(self, mock_log): - run_thread = eventlet.spawn(_run_scheduler) - result = run_thread.wait() + run_thread = concurrency.spawn(_run_scheduler) + result = concurrency.wait(run_thread) self.assertEqual(result, 1) @@ -62,8 +63,8 @@ def test_service_exits_correctly_on_fatal_exception_in_handler_run(self, mock_lo def test_service_exits_correctly_on_fatal_exception_in_handler_cleanup( self, mock_log ): - run_thread = eventlet.spawn(_run_scheduler) - result = run_thread.wait() + run_thread = concurrency.spawn(_run_scheduler) + result = concurrency.wait(run_thread) self.assertEqual(result, 1) @@ -75,8 +76,8 @@ def test_service_exits_correctly_on_fatal_exception_in_handler_cleanup( def test_service_exits_correctly_on_fatal_exception_in_entrypoint_start( self, mock_log ): - run_thread = eventlet.spawn(_run_scheduler) - result = run_thread.wait() + run_thread = concurrency.spawn(_run_scheduler) + result = concurrency.wait(run_thread) self.assertEqual(result, 1) diff --git a/st2actions/tests/unit/test_scheduler_retry.py b/st2actions/tests/unit/test_scheduler_retry.py index d975964bc6..cf5df656f2 100644 --- a/st2actions/tests/unit/test_scheduler_retry.py +++ b/st2actions/tests/unit/test_scheduler_retry.py @@ -16,8 +16,9 @@ # This import must be first for import-time side-effects. from st2tests.base import CleanDbTestCase -import eventlet import mock + +from st2common.util import concurrency import pymongo import uuid @@ -42,38 +43,38 @@ class SchedulerHandlerRetryTestCase(CleanDbTestCase): side_effect=[pymongo.errors.ConnectionFailure(), MOCK_QUEUE_ITEM] ), ) - @mock.patch.object(eventlet.GreenPool, "spawn", mock.MagicMock(return_value=None)) + @mock.patch.object(concurrency.get_green_pool_class(), "spawn", mock.MagicMock(return_value=None)) def test_handler_retry_connection_error(self): scheduling_queue_handler = handler.ActionExecutionSchedulingQueueHandler() scheduling_queue_handler.process() # Make sure retry occurs and that _handle_execution in process is called. calls = [mock.call(scheduling_queue_handler._handle_execution, MOCK_QUEUE_ITEM)] - eventlet.GreenPool.spawn.assert_has_calls(calls) + concurrency.get_green_pool_class().spawn.assert_has_calls(calls) @mock.patch.object( handler.ActionExecutionSchedulingQueueHandler, "_get_next_execution", mock.MagicMock(side_effect=[pymongo.errors.ConnectionFailure()] * 3), ) - @mock.patch.object(eventlet.GreenPool, "spawn", mock.MagicMock(return_value=None)) + @mock.patch.object(concurrency.get_green_pool_class(), "spawn", mock.MagicMock(return_value=None)) def test_handler_retries_exhausted(self): scheduling_queue_handler = handler.ActionExecutionSchedulingQueueHandler() self.assertRaises( pymongo.errors.ConnectionFailure, scheduling_queue_handler.process ) - self.assertEqual(eventlet.GreenPool.spawn.call_count, 0) + self.assertEqual(concurrency.get_green_pool_class().spawn.call_count, 0) @mock.patch.object( handler.ActionExecutionSchedulingQueueHandler, "_get_next_execution", mock.MagicMock(side_effect=KeyError()), ) - @mock.patch.object(eventlet.GreenPool, "spawn", mock.MagicMock(return_value=None)) + @mock.patch.object(concurrency.get_green_pool_class(), "spawn", mock.MagicMock(return_value=None)) def test_handler_retry_unexpected_error(self): scheduling_queue_handler = handler.ActionExecutionSchedulingQueueHandler() self.assertRaises(KeyError, scheduling_queue_handler.process) - self.assertEqual(eventlet.GreenPool.spawn.call_count, 0) + self.assertEqual(concurrency.get_green_pool_class().spawn.call_count, 0) @mock.patch.object( ex_q_db_access.ActionExecutionSchedulingQueue, diff --git a/st2actions/tests/unit/test_worker.py b/st2actions/tests/unit/test_worker.py index 917d0683e1..9480064244 100644 --- a/st2actions/tests/unit/test_worker.py +++ b/st2actions/tests/unit/test_worker.py @@ -16,8 +16,9 @@ from __future__ import absolute_import from bson.errors import InvalidStringData -import eventlet import mock + +from st2common.util import concurrency import os from oslo_config import cfg from tooz.drivers.redis import RedisDriver @@ -168,11 +169,11 @@ def test_worker_shutdown(self): ) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) - runner_thread = eventlet.spawn(action_worker._run_action, liveaction_db) + runner_thread = concurrency.spawn(action_worker._run_action, liveaction_db) # Wait for the worker up to 10s to add the liveaction to _running_liveactions. for i in range(0, int(10 / 0.1)): - eventlet.sleep(0.1) + concurrency.sleep(0.1) if len(action_worker._running_liveactions) > 0: break @@ -231,25 +232,25 @@ def test_worker_graceful_shutdown_with_multiple_runners(self): ) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) - runner_thread = eventlet.spawn(action_worker._run_action, liveaction_db) + runner_thread = concurrency.spawn(action_worker._run_action, liveaction_db) # Wait for the worker up to 10s to add the liveaction to _running_liveactions. for i in range(0, int(10 / 0.1)): - eventlet.sleep(0.1) + concurrency.sleep(0.1) if len(action_worker._running_liveactions) > 0: break self.assertEqual(len(action_worker._running_liveactions), 1) # Shutdown the worker to trigger the abandon process. - shutdown_thread = eventlet.spawn(action_worker.shutdown) + shutdown_thread = concurrency.spawn(action_worker.shutdown) # Make sure the temporary file has been deleted. self.assertFalse(os.path.isfile(temp_file)) # Wait for the worker up to 10s to remove the liveaction from _running_liveactions. for i in range(0, int(10 / 0.1)): - eventlet.sleep(0.1) + concurrency.sleep(0.1) if len(action_worker._running_liveactions) < 1: break liveaction_db = LiveAction.get_by_id(liveaction_db.id) @@ -294,27 +295,27 @@ def test_worker_graceful_shutdown_with_single_runner(self): ) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) - runner_thread = eventlet.spawn(action_worker._run_action, liveaction_db) + runner_thread = concurrency.spawn(action_worker._run_action, liveaction_db) # Wait for the worker up to 10s to add the liveaction to _running_liveactions. for i in range(0, int(10 / 0.1)): - eventlet.sleep(0.1) + concurrency.sleep(0.1) if len(action_worker._running_liveactions) > 0: break self.assertEqual(len(action_worker._running_liveactions), 1) # Shutdown the worker to trigger the abandon process. - shutdown_thread = eventlet.spawn(action_worker.shutdown) + shutdown_thread = concurrency.spawn(action_worker.shutdown) # Wait for action runner shutdown sequence to complete - eventlet.sleep(5) + concurrency.sleep(5) # Make sure the temporary file has been deleted. self.assertFalse(os.path.isfile(temp_file)) # Wait for the worker up to 10s to remove the liveaction from _running_liveactions. for i in range(0, int(10 / 0.1)): - eventlet.sleep(0.1) + concurrency.sleep(0.1) if len(action_worker._running_liveactions) < 1: break liveaction_db = LiveAction.get_by_id(liveaction_db.id) @@ -360,27 +361,27 @@ def test_worker_graceful_shutdown_exit_timeout(self): ) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) - runner_thread = eventlet.spawn(action_worker._run_action, liveaction_db) + runner_thread = concurrency.spawn(action_worker._run_action, liveaction_db) # Wait for the worker up to 10s to add the liveaction to _running_liveactions. for i in range(0, int(10 / 0.1)): - eventlet.sleep(0.1) + concurrency.sleep(0.1) if len(action_worker._running_liveactions) > 0: break self.assertEqual(len(action_worker._running_liveactions), 1) # Shutdown the worker to trigger the abandon process. - shutdown_thread = eventlet.spawn(action_worker.shutdown) + shutdown_thread = concurrency.spawn(action_worker.shutdown) # Continue the excution for 5+ seconds to ensure timeout occurs. - eventlet.sleep(6) + concurrency.sleep(6) # Make sure the temporary file has been deleted. self.assertFalse(os.path.isfile(temp_file)) # Wait for the worker up to 10s to remove the liveaction from _running_liveactions. for i in range(0, int(10 / 0.1)): - eventlet.sleep(0.1) + concurrency.sleep(0.1) if len(action_worker._running_liveactions) < 1: break liveaction_db = LiveAction.get_by_id(liveaction_db.id) diff --git a/st2actions/tests/unit/test_workflow_engine.py b/st2actions/tests/unit/test_workflow_engine.py index ed6440bd57..2f4344a4c4 100644 --- a/st2actions/tests/unit/test_workflow_engine.py +++ b/st2actions/tests/unit/test_workflow_engine.py @@ -15,9 +15,10 @@ from __future__ import absolute_import -import eventlet import mock +from st2common.util import concurrency + # This import must be early for import-time side-effects. import st2tests @@ -290,7 +291,7 @@ def test_process_error_handling_has_error(self, mock_get_lock): self.assertEqual(lv_ac_db.status, action_constants.LIVEACTION_STATUS_RUNNING) # Sleep up to the test config gc_max_idle_sec before running gc. - eventlet.sleep(cfg.CONF.workflow_engine.gc_max_idle_sec) + concurrency.sleep(cfg.CONF.workflow_engine.gc_max_idle_sec) # Run garbage collection. gc = garbage_collector.GarbageCollectorService() @@ -320,10 +321,10 @@ def test_workflow_engine_shutdown(self): )[0] self.assertEqual(wf_ex_db.status, action_constants.LIVEACTION_STATUS_RUNNING) workflow_engine = workflows.get_engine() - eventlet.spawn(workflow_engine.shutdown) + concurrency.spawn(workflow_engine.shutdown) # Sleep for few seconds to ensure execution transitions to pausing. - eventlet.sleep(8) + concurrency.sleep(8) lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) self.assertEqual(lv_ac_db.status, action_constants.LIVEACTION_STATUS_PAUSING) @@ -349,7 +350,7 @@ def test_workflow_engine_shutdown(self): workflow_engine = workflows.get_engine() workflow_engine._delay = 0 workflow_engine.start(False) - eventlet.sleep(workflow_engine._delay + 5) + concurrency.sleep(workflow_engine._delay + 5) lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) self.assertTrue( lv_ac_db.status @@ -383,10 +384,10 @@ def test_workflow_engine_shutdown_with_multiple_members(self): self.assertEqual(wf_ex_db.status, action_constants.LIVEACTION_STATUS_RUNNING) workflow_engine = workflows.get_engine() - eventlet.spawn(workflow_engine.shutdown) + concurrency.spawn(workflow_engine.shutdown) # Sleep for few seconds to ensure shutdown sequence completes. - eventlet.sleep(5) + concurrency.sleep(5) lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) self.assertEqual(lv_ac_db.status, action_constants.LIVEACTION_STATUS_RUNNING) @@ -425,10 +426,10 @@ def test_workflow_engine_shutdown_with_service_registry_disabled(self): self.assertEqual(wf_ex_db.status, action_constants.LIVEACTION_STATUS_RUNNING) workflow_engine = workflows.get_engine() - eventlet.spawn(workflow_engine.shutdown) + concurrency.spawn(workflow_engine.shutdown) # Sleep for few seconds to ensure shutdown sequence completes. - eventlet.sleep(5) + concurrency.sleep(5) # WFE doesn't pause the workflow, since service registry is disabled. lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) @@ -457,11 +458,11 @@ def test_workflow_engine_shutdown_first_then_start(self): workflow_engine._delay = 5 # Initiate shutdown first - eventlet.spawn(workflow_engine.shutdown) - eventlet.spawn_after(1, workflow_engine.start, True) + concurrency.spawn(workflow_engine.shutdown) + concurrency.spawn_after(1, workflow_engine.start, True) # Sleep for few seconds to ensure shutdown sequence completes. - eventlet.sleep(2) + concurrency.sleep(2) lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) # Shutdown routine acquires the lock first @@ -476,7 +477,7 @@ def test_workflow_engine_shutdown_first_then_start(self): workflows.get_engine().process(t1_ac_ex_db) # Startup sequence won't proceed until shutdown routine completes. # Assuming shutdown sequence is complete, start up sequence will resume the workflow. - eventlet.sleep(workflow_engine._delay + 5) + concurrency.sleep(workflow_engine._delay + 5) lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) self.assertTrue( lv_ac_db.status @@ -514,12 +515,12 @@ def test_workflow_engine_start_first_then_shutdown(self): workflow_engine._delay = 0 # Initiate start first - eventlet.spawn(workflow_engine.start, True) - eventlet.spawn_after(1, workflow_engine.shutdown) + concurrency.spawn(workflow_engine.start, True) + concurrency.spawn_after(1, workflow_engine.shutdown) lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) # Startup routine acquires the lock first and shutdown routine sees a new member present in registry. - eventlet.sleep(workflow_engine._delay + 5) + concurrency.sleep(workflow_engine._delay + 5) lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id)) self.assertEqual(lv_ac_db.status, action_constants.LIVEACTION_STATUS_RUNNING) diff --git a/st2api/tests/integration/test_gunicorn_configs.py b/st2api/tests/integration/test_gunicorn_configs.py index c7df6d3978..0ca3fee2c1 100644 --- a/st2api/tests/integration/test_gunicorn_configs.py +++ b/st2api/tests/integration/test_gunicorn_configs.py @@ -18,10 +18,12 @@ from six.moves import http_client import requests -import eventlet -from eventlet.green import subprocess import pytest +from st2common.util import concurrency + +subprocess = concurrency.get_subprocess_module() + import st2tests.config from st2common.models.utils import profiling from st2common.util.shell import kill_process @@ -48,7 +50,7 @@ def test_st2api_wsgi_entry_point(self): process = subprocess.Popen(cmd, env=env, shell=True, preexec_fn=os.setsid) try: self.add_process(process=process) - eventlet.sleep(8) + concurrency.sleep(8) self.assertProcessIsRunning(process=process) response = requests.get("http://127.0.0.1:%s/v1/actions" % (port)) self.assertEqual(response.status_code, http_client.OK) @@ -70,7 +72,7 @@ def test_st2auth(self): process = subprocess.Popen(cmd, env=env, shell=True, preexec_fn=os.setsid) try: self.add_process(process=process) - eventlet.sleep(8) + concurrency.sleep(8) self.assertProcessIsRunning(process=process) response = requests.post("http://127.0.0.1:%s/tokens" % (port)) self.assertEqual(response.status_code, http_client.UNAUTHORIZED) diff --git a/st2client/st2client/utils/httpclient.py b/st2client/st2client/utils/httpclient.py index ec7bebdf64..de61775946 100644 --- a/st2client/st2client/utils/httpclient.py +++ b/st2client/st2client/utils/httpclient.py @@ -17,7 +17,7 @@ import json import logging -from pipes import quote as pquote +from shlex import quote as pquote import requests diff --git a/st2common/st2common/stream/listener.py b/st2common/st2common/stream/listener.py index 5eb5dc8b8f..b79c2c477f 100644 --- a/st2common/st2common/stream/listener.py +++ b/st2common/st2common/stream/listener.py @@ -16,7 +16,7 @@ from __future__ import absolute_import import fnmatch -import eventlet +from st2common.util import concurrency from kombu.mixins import ConsumerMixin from oslo_config import cfg @@ -94,7 +94,7 @@ def generator( end_statuses=None, end_execution_id=None, ): - queue = eventlet.Queue() + queue = concurrency.Queue() queue.put("") self.queues.append(queue) try: @@ -144,7 +144,7 @@ def generator( continue yield message - except eventlet.queue.Empty: + except concurrency.get_queue_empty_exception(): yield finally: self.queues.remove(queue) @@ -273,13 +273,13 @@ def get_listener(name): if not _stream_listener: with transport_utils.get_connection() as conn: _stream_listener = StreamListener(conn) - eventlet.spawn_n(listen, _stream_listener) + concurrency.spawn(listen, _stream_listener) return _stream_listener elif name == "execution_output": if not _execution_output_listener: with transport_utils.get_connection() as conn: _execution_output_listener = ExecutionOutputListener(conn) - eventlet.spawn_n(listen, _execution_output_listener) + concurrency.spawn(listen, _execution_output_listener) return _execution_output_listener else: raise ValueError("Invalid listener name: %s" % (name)) diff --git a/st2common/st2common/util/concurrency.py b/st2common/st2common/util/concurrency.py index edad8770e1..c4b1bccbb6 100644 --- a/st2common/st2common/util/concurrency.py +++ b/st2common/st2common/util/concurrency.py @@ -20,6 +20,8 @@ "set_concurrency_library" function. """ +import os + try: import eventlet # pylint: disable=import-error except ImportError: @@ -27,11 +29,13 @@ try: import gevent # pylint: disable=import-error # pants: no-infer-dep + import gevent.lock import gevent.pool + import gevent.queue except ImportError: gevent = None -CONCURRENCY_LIBRARY = "eventlet" +CONCURRENCY_LIBRARY = os.environ.get("ST2_CONCURRENCY_LIBRARY", "eventlet") __all__ = [ "set_concurrency_library", @@ -76,9 +80,6 @@ def get_subprocess_module(): else: raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") -# socket, app.setup_app(), log=LOG, log_output=False -# sock, app.setup_app(), custom_pool=worker_pool, log=LOG, log_output=False -# sock, app.setup_app(), custom_pool=worker_pool def wsgi_server(socket, app, custom_pool=None, log=None, log_output=True, *args, **kwargs): if CONCURRENCY_LIBRARY == "eventlet": from eventlet import wsgi @@ -87,8 +88,8 @@ def wsgi_server(socket, app, custom_pool=None, log=None, log_output=True, *args, elif CONCURRENCY_LIBRARY == "gevent": from gevent import pywsgi - # Figure out how to handle custom pool - pywsgi.WSGIServer(socket, app) + server = pywsgi.WSGIServer(socket, app, spawn=custom_pool, log=log) + server.serve_forever() else: raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") @@ -105,6 +106,24 @@ def subprocess_popen(*args, **kwargs): raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") +def spawn_after(seconds, func, *args, **kwargs): + if CONCURRENCY_LIBRARY == "eventlet": + return eventlet.spawn_after(seconds, func, *args, **kwargs) + elif CONCURRENCY_LIBRARY == "gevent": + return gevent.spawn_later(seconds, func, *args, **kwargs) + else: + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + + +def Semaphore(*args, **kwargs): + if CONCURRENCY_LIBRARY == "eventlet": + return eventlet.Semaphore(*args, **kwargs) + elif CONCURRENCY_LIBRARY == "gevent": + return gevent.lock.Semaphore(*args, **kwargs) + else: + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + + def spawn(func, *args, **kwargs): if CONCURRENCY_LIBRARY == "eventlet": return eventlet.spawn(func, *args, **kwargs) @@ -142,10 +161,23 @@ def kill(green_thread, *args, **kwargs): def listen(host, port): + return listen_server(host, port) + + +def Queue(*args, **kwargs): if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.listen((host, port)) + return eventlet.Queue(*args, **kwargs) elif CONCURRENCY_LIBRARY == "gevent": - raise NotImplementedError + return gevent.queue.Queue(*args, **kwargs) + else: + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + + +def get_queue_empty_exception(): + if CONCURRENCY_LIBRARY == "eventlet": + return eventlet.queue.Empty + elif CONCURRENCY_LIBRARY == "gevent": + return gevent.queue.Empty else: raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") @@ -187,18 +219,49 @@ def get_green_pool_class(): raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") -def is_green_pool_free(pool): +def green_pool_free_count(pool): """ - Return True if the provided green pool is free, False otherwise. + Return the number of free slots in the pool. """ if CONCURRENCY_LIBRARY == "eventlet": return pool.free() elif CONCURRENCY_LIBRARY == "gevent": - return not pool.full() + return pool.free_count() else: raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") +def is_green_pool_free(pool): + """ + Return True if the provided green pool has at least one free slot, False otherwise. + """ + return green_pool_free_count(pool) > 0 + + +def green_pool_running_count(pool): + """ + Return the number of greenlets currently running in the pool. + """ + if CONCURRENCY_LIBRARY == "eventlet": + return pool.running() + elif CONCURRENCY_LIBRARY == "gevent": + return len(pool.greenlets) + else: + raise ValueError("Unsupported concurrency library") + + +def get_pool_greenlets(pool): + """ + Return the set of currently running greenlets in the pool. + """ + if CONCURRENCY_LIBRARY == "eventlet": + return pool.coroutines_running + elif CONCURRENCY_LIBRARY == "gevent": + return pool.greenlets + else: + raise ValueError("Unsupported concurrency library") + + def green_pool_wait_all(pool): """ Wait for all the green threads in the pool to finish. @@ -224,8 +287,10 @@ def listen_server(host, port, backlog=50, **kwargs): import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((host, port)) - return sock.listen(backlog) + sock.listen(backlog) + return sock else: raise ValueError("Unsupported concurrency library") diff --git a/st2common/st2common/util/greenpooldispatch.py b/st2common/st2common/util/greenpooldispatch.py index 156d530116..61dff7274c 100644 --- a/st2common/st2common/util/greenpooldispatch.py +++ b/st2common/st2common/util/greenpooldispatch.py @@ -16,9 +16,10 @@ from __future__ import absolute_import import time -import eventlet import six.moves.queue +from st2common.util import concurrency + from st2common import log as logging __all__ = ["BufferedDispatcher"] @@ -44,8 +45,8 @@ def __init__( name=None, ): self._pool_limit = dispatch_pool_size - self._dispatcher_pool = eventlet.GreenPool(dispatch_pool_size) - self._dispatch_monitor_thread = eventlet.greenthread.spawn(self._flush) + self._dispatcher_pool = concurrency.get_green_pool_class()(dispatch_pool_size) + self._dispatch_monitor_thread = concurrency.spawn(self._flush) self._monitor_thread_empty_q_sleep_time = monitor_thread_empty_q_sleep_time self._monitor_thread_no_workers_sleep_time = ( monitor_thread_no_workers_sleep_time @@ -71,13 +72,13 @@ def shutdown(self): def _flush(self): while True: while self._work_buffer.empty(): - eventlet.greenthread.sleep(self._monitor_thread_empty_q_sleep_time) - while self._dispatcher_pool.free() <= 0: - eventlet.greenthread.sleep(self._monitor_thread_no_workers_sleep_time) + concurrency.sleep(self._monitor_thread_empty_q_sleep_time) + while not concurrency.is_green_pool_free(self._dispatcher_pool): + concurrency.sleep(self._monitor_thread_no_workers_sleep_time) self._flush_now() def _flush_now(self): - if self._dispatcher_pool.free() <= 0: + if not concurrency.is_green_pool_free(self._dispatcher_pool): now = time.time() if (now - self._pool_last_free_ts) >= POOL_BUSY_THRESHOLD_SECONDS: @@ -90,12 +91,12 @@ def _flush_now(self): # Update the time of when there were free threads available self._pool_last_free_ts = time.time() - while not self._work_buffer.empty() and self._dispatcher_pool.free() > 0: + while not self._work_buffer.empty() and concurrency.is_green_pool_free(self._dispatcher_pool): (handler, args) = self._work_buffer.get_nowait() self._dispatcher_pool.spawn(handler, *args) def __repr__(self): - free_count = self._dispatcher_pool.free() + free_count = concurrency.green_pool_free_count(self._dispatcher_pool) values = ( self.name, self._pool_limit, diff --git a/st2common/st2common/util/wsgi.py b/st2common/st2common/util/wsgi.py index 63ec6c6253..b7b77f3ba5 100644 --- a/st2common/st2common/util/wsgi.py +++ b/st2common/st2common/util/wsgi.py @@ -14,13 +14,13 @@ # limitations under the License. """ -Eventlet WSGI server related utility functions. +WSGI server related utility functions. """ from __future__ import absolute_import -import eventlet from st2common import log as logging +from st2common.util import concurrency LOG = logging.getLogger(__name__) @@ -43,17 +43,17 @@ def shutdown_server_kill_pending_requests(sock, worker_pool, wait_time=2): worker_pool.resize(0) sock.close() - active_requests = worker_pool.running() + active_requests = concurrency.green_pool_running_count(worker_pool) LOG.info("Shutting down. Requests left: %s", active_requests) # Give active requests some time to finish if active_requests > 0: - eventlet.sleep(wait_time) + concurrency.sleep(wait_time) # Kill requests which still didn't finish - running_corutines = worker_pool.coroutines_running.copy() - for coro in running_corutines: - eventlet.greenthread.kill(coro) + running_greenlets = concurrency.get_pool_greenlets(worker_pool).copy() + for coro in running_greenlets: + concurrency.kill(coro) LOG.info("Exiting...") raise SystemExit() diff --git a/st2common/tests/integration/test_logging.py b/st2common/tests/integration/test_logging.py index 92069db2da..aafe2f83b4 100644 --- a/st2common/tests/integration/test_logging.py +++ b/st2common/tests/integration/test_logging.py @@ -26,8 +26,9 @@ import signal import pytest -import eventlet -from eventlet.green import subprocess +from st2common.util import concurrency + +subprocess = concurrency.get_subprocess_module() from st2tests.base import IntegrationTestCase @@ -54,7 +55,7 @@ def test_formatting_with_unicode_data_works_no_stdout_patching_valid_utf8_encodi self.add_process(process=process) # Give it some time to start up and run for a while - eventlet.sleep(2) + concurrency.sleep(2) process.send_signal(signal.SIGKILL) stdout = process.stdout.read().decode("utf-8").strip() @@ -96,7 +97,7 @@ def test_formatting_with_unicode_data_works_no_stdout_patching_non_valid_utf8_en self.add_process(process=process) # Give it some time to start up and run for a while - eventlet.sleep(2) + concurrency.sleep(2) process.send_signal(signal.SIGKILL) stdout = process.stdout.read().decode("utf-8") @@ -141,7 +142,7 @@ def test_formatting_with_unicode_data_works_no_stdout_patching_ascii_pythonioenc self.add_process(process=process) # Give it some time to start up and run for a while - eventlet.sleep(2) + concurrency.sleep(2) process.send_signal(signal.SIGKILL) stdout = process.stdout.read().decode("utf-8") @@ -184,7 +185,7 @@ def test_formatting_with_unicode_data_works_with_stdout_patching_valid_utf8_enco self.add_process(process=process) # Give it some time to start up and run for a while - eventlet.sleep(2) + concurrency.sleep(2) process.send_signal(signal.SIGKILL) stdout = process.stdout.read().decode("utf-8") @@ -219,7 +220,7 @@ def test_formatting_with_unicode_data_works_with_stdout_patching_non_valid_utf8_ self.add_process(process=process) # Give it some time to start up and run for a while - eventlet.sleep(2) + concurrency.sleep(2) process.send_signal(signal.SIGKILL) stdout = process.stdout.read().decode("utf-8") @@ -255,7 +256,7 @@ def test_formatting_with_unicode_data_works_with_stdout_patching__ascii_pythonio self.add_process(process=process) # Give it some time to start up and run for a while - eventlet.sleep(2) + concurrency.sleep(2) process.send_signal(signal.SIGKILL) stdout = process.stdout.read().decode("utf-8") diff --git a/st2common/tests/integration/test_service_setup_log_level_filtering.py b/st2common/tests/integration/test_service_setup_log_level_filtering.py index 869a26260c..23369af339 100644 --- a/st2common/tests/integration/test_service_setup_log_level_filtering.py +++ b/st2common/tests/integration/test_service_setup_log_level_filtering.py @@ -18,8 +18,9 @@ import sys import signal -import eventlet -from eventlet.green import subprocess +from st2common.util import concurrency + +subprocess = concurrency.get_subprocess_module() import st2tests.config from st2tests.base import IntegrationTestCase @@ -83,7 +84,7 @@ def test_system_info_is_logged_on_startup(self): self.add_process(process=process) # Give it some time to start up - eventlet.sleep(4) + concurrency.sleep(4) process.send_signal(signal.SIGKILL) # Verify first 4 environment related log messages @@ -105,7 +106,7 @@ def test_warning_is_emitted_on_non_utf8_encoding(self): self.add_process(process=process) # Give it some time to start up - eventlet.sleep(4) + concurrency.sleep(4) process.send_signal(signal.SIGKILL) # Verify first 4 environment related log messages @@ -126,7 +127,7 @@ def test_audit_log_level_is_filtered_if_log_level_is_not_debug_or_audit(self): self.add_process(process=process) # Give it some time to start up - eventlet.sleep(4) + concurrency.sleep(4) process.send_signal(signal.SIGKILL) # Verify first 4 environment related log messages @@ -143,7 +144,7 @@ def test_audit_log_level_is_filtered_if_log_level_is_not_debug_or_audit(self): self.add_process(process=process) # Give it some time to start up - eventlet.sleep(4) + concurrency.sleep(4) process.send_signal(signal.SIGKILL) # First 6 log lines are debug messages about the environment which are always logged @@ -158,7 +159,7 @@ def test_audit_log_level_is_filtered_if_log_level_is_not_debug_or_audit(self): self.add_process(process=process) # Give it some time to start up - eventlet.sleep(6) + concurrency.sleep(6) process.send_signal(signal.SIGKILL) # First 6 log lines are debug messages about the environment which are always logged @@ -173,7 +174,7 @@ def test_audit_log_level_is_filtered_if_log_level_is_not_debug_or_audit(self): self.add_process(process=process) # Give it some time to start up - eventlet.sleep(6) + concurrency.sleep(6) process.send_signal(signal.SIGKILL) # First 6 log lines are debug messages about the environment which are always logged @@ -188,7 +189,7 @@ def test_audit_log_level_is_filtered_if_log_level_is_not_debug_or_audit(self): self.add_process(process=process) # Give it some time to start up - eventlet.sleep(6) + concurrency.sleep(6) process.send_signal(signal.SIGKILL) # First 6 log lines are debug messages about the environment which are always logged @@ -207,7 +208,7 @@ def test_kombu_heartbeat_tick_log_messages_are_excluded(self): self.add_process(process=process) # Give it some time to start up - eventlet.sleep(6) + concurrency.sleep(6) process.send_signal(signal.SIGKILL) stdout = "\n".join(process.stdout.read().decode("utf-8").split("\n")) @@ -218,7 +219,7 @@ def test_kombu_heartbeat_tick_log_messages_are_excluded(self): self.add_process(process=process) # Give it some time to start up - eventlet.sleep(6) + concurrency.sleep(6) process.send_signal(signal.SIGKILL) stdout = "\n".join(process.stdout.read().decode("utf-8").split("\n")) diff --git a/st2common/tests/unit/test_greenpooldispatch.py b/st2common/tests/unit/test_greenpooldispatch.py index f77ae2937e..8ad17c2b17 100644 --- a/st2common/tests/unit/test_greenpooldispatch.py +++ b/st2common/tests/unit/test_greenpooldispatch.py @@ -14,9 +14,9 @@ # limitations under the License. from __future__ import absolute_import -import eventlet import mock +from st2common.util import concurrency from st2common.util.greenpooldispatch import BufferedDispatcher from unittest import TestCase from six.moves import range @@ -31,7 +31,7 @@ def test_dispatch_simple(self): dispatcher.dispatch(mock_handler, i, i + 1) expected.append((i, i + 1)) while mock_handler.call_count < 10: - eventlet.sleep(0.01) + concurrency.sleep(0.01) dispatcher.shutdown() call_args_list = [ (args[0][0], args[0][1]) for args in mock_handler.call_args_list @@ -50,7 +50,7 @@ def test_dispatch_starved(self): dispatcher.dispatch(mock_handler, i, i + 1) expected.append((i, i + 1)) while mock_handler.call_count < 10: - eventlet.sleep(0.01) + concurrency.sleep(0.01) dispatcher.shutdown() call_args_list = [ (args[0][0], args[0][1]) for args in mock_handler.call_args_list diff --git a/st2common/tests/unit/test_stream_generator.py b/st2common/tests/unit/test_stream_generator.py index 39a67621b5..ad5997d7ed 100644 --- a/st2common/tests/unit/test_stream_generator.py +++ b/st2common/tests/unit/test_stream_generator.py @@ -54,7 +54,7 @@ def get_consumers(self, consumer, channel): class TestStream(unittest.TestCase): @mock.patch("st2common.stream.listener.BaseListener._get_action_ref_for_body") - @mock.patch("eventlet.Queue") + @mock.patch("st2common.util.concurrency.Queue") def test_generator(self, mock_queue, get_action_ref_for_body): get_action_ref_for_body.return_value = None mock_queue.return_value = MockQueue() diff --git a/st2common/tests/unit/test_transport.py b/st2common/tests/unit/test_transport.py index 71d18e6010..abade10e0d 100644 --- a/st2common/tests/unit/test_transport.py +++ b/st2common/tests/unit/test_transport.py @@ -21,7 +21,8 @@ import random import unittest -import eventlet + +from st2common.util import concurrency from bson.objectid import ObjectId from kombu.mixins import ConsumerMixin @@ -77,16 +78,16 @@ def test_publish_compression(self): with transport_utils.get_connection() as connection: connection.connect() watcher = QueueConsumer(connection=connection, queue=queue) - watcher_thread = eventlet.greenthread.spawn(watcher.run) + watcher_thread = concurrency.spawn(watcher.run) # Give it some time to start up since we are publishing on a new queue - eventlet.sleep(0.5) + concurrency.sleep(0.5) self.assertEqual(len(watcher.received_messages), 0) # 1. Verify compression is off as a default publisher.publish(payload=live_action_db, exchange=exchange) - eventlet.sleep(0.2) + concurrency.sleep(0.2) self.assertEqual(len(watcher.received_messages), 1) self.assertEqual( @@ -105,7 +106,7 @@ def test_publish_compression(self): cfg.CONF.set_override(name="compression", group="messaging", override="zstd") publisher.publish(payload=live_action_db, exchange=exchange) - eventlet.sleep(0.2) + concurrency.sleep(0.2) self.assertEqual(len(watcher.received_messages), 2) self.assertEqual( @@ -125,7 +126,7 @@ def test_publish_compression(self): cfg.CONF.set_override(name="compression", group="messaging", override="zstd") publisher.publish(payload=live_action_db, exchange=exchange, compression="gzip") - eventlet.sleep(0.2) + concurrency.sleep(0.2) self.assertEqual(len(watcher.received_messages), 3) self.assertEqual( diff --git a/st2reactor/tests/unit/test_sensor_wrapper.py b/st2reactor/tests/unit/test_sensor_wrapper.py index 36b8c04f10..16ae747d23 100644 --- a/st2reactor/tests/unit/test_sensor_wrapper.py +++ b/st2reactor/tests/unit/test_sensor_wrapper.py @@ -26,6 +26,8 @@ import mock import eventlet +from st2common.util import concurrency + import st2tests.config as tests_config from st2tests.base import TESTS_CONFIG_PATH @@ -203,6 +205,11 @@ def test_sensor_wrapper_poll_method_still_works(self): # 'poll'" will be thrown import select - self.assertTrue(eventlet.patcher.is_monkey_patched(select)) - self.assertTrue(select != eventlet.patcher.original("select")) + if concurrency.get_concurrency_library() == "eventlet": + self.assertTrue(eventlet.patcher.is_monkey_patched(select)) + self.assertTrue(select != eventlet.patcher.original("select")) + else: + import gevent.monkey + + self.assertTrue(gevent.monkey.is_module_patched("select")) self.assertTrue(select.poll()) diff --git a/st2stream/st2stream/cmd/api.py b/st2stream/st2stream/cmd/api.py index 3b4b393695..9658085818 100644 --- a/st2stream/st2stream/cmd/api.py +++ b/st2stream/st2stream/cmd/api.py @@ -79,7 +79,7 @@ def _run_server(): def queue_shutdown(signal_number, stack_frame): deregister_service(STREAM) - concurrent.spawn( + concurrency.spawn( shutdown_server_kill_pending_requests, sock=sock, worker_pool=worker_pool, diff --git a/st2stream/tests/unit/controllers/v1/test_stream_execution_output.py b/st2stream/tests/unit/controllers/v1/test_stream_execution_output.py index 9a135d1789..fe8116f80a 100644 --- a/st2stream/tests/unit/controllers/v1/test_stream_execution_output.py +++ b/st2stream/tests/unit/controllers/v1/test_stream_execution_output.py @@ -15,7 +15,7 @@ import json -import eventlet +from st2common.util import concurrency from six.moves import http_client @@ -48,7 +48,7 @@ def test_get_output_running_execution(self): # early enough for tests to pass. # NOTE: This only affects tests where listeners are not pre-initialized. listener = get_listener(name="execution_output") - eventlet.sleep(0.5) + concurrency.sleep(0.5) # Test the execution output API endpoint for execution which is running (blocking) status = action_constants.LIVEACTION_STATUS_RUNNING @@ -89,14 +89,14 @@ def publish_action_finished(action_execution_db): output_db = ActionExecutionOutputDB(**output_params) ActionExecutionOutput.add_or_update(output_db) - eventlet.sleep(0.5) + concurrency.sleep(0.5) # Transition execution to completed state so the connection closes action_execution_db.status = action_constants.LIVEACTION_STATUS_SUCCEEDED action_execution_db = ActionExecution.add_or_update(action_execution_db) - eventlet.spawn_after(0.2, insert_mock_data) - eventlet.spawn_after(1.0, publish_action_finished, action_execution_db) + concurrency.spawn_after(0.2, insert_mock_data) + concurrency.spawn_after(1.0, publish_action_finished, action_execution_db) # Retrieve data while execution is running - endpoint return new data once it's available # and block until the execution finishes diff --git a/st2tests/st2tests/base.py b/st2tests/st2tests/base.py index b452f79983..d28f634239 100644 --- a/st2tests/st2tests/base.py +++ b/st2tests/st2tests/base.py @@ -38,6 +38,7 @@ import six import eventlet import psutil +from st2common.util import concurrency import mock from oslo_config import cfg from unittest import TestCase @@ -186,19 +187,14 @@ class EventletTestCase(TestCase): @classmethod def setUpClass(cls): - eventlet.monkey_patch( - os=True, - select=True, - socket=True, - thread=False if "--use-debugger" in sys.argv else True, - time=True, - ) + monkey_patch() @classmethod def tearDownClass(cls): - eventlet.monkey_patch( - os=False, select=False, socket=False, thread=False, time=False - ) + if concurrency.get_concurrency_library() == "eventlet": + eventlet.monkey_patch( + os=False, select=False, socket=False, thread=False, time=False + ) class BaseDbTestCase(BaseTestCase): @@ -360,7 +356,7 @@ def _wait_on_status( assert isinstance(status, six.string_types), "%s is not of text type" % (status) for _ in range(0, retries): - eventlet.sleep(delay) + concurrency.sleep(delay) liveaction_db = LiveAction.get_by_id(str(liveaction_db.id)) if liveaction_db.status == status: break @@ -378,7 +374,7 @@ def _wait_on_statuses( ) for _ in range(0, retries): - eventlet.sleep(delay) + concurrency.sleep(delay) liveaction_db = LiveAction.get_by_id(str(liveaction_db.id)) if liveaction_db.status in statuses: break @@ -392,7 +388,7 @@ def _wait_on_ac_ex_status( self, execution_db, status, retries=300, delay=0.1, raise_exc=True ): for _ in range(0, retries): - eventlet.sleep(delay) + concurrency.sleep(delay) execution_db = ex_db_access.ActionExecution.get_by_id(str(execution_db.id)) if execution_db.status == status: break @@ -406,7 +402,7 @@ def _wait_on_call_count( self, mocked, expected_count, retries=100, delay=0.1, raise_exc=True ): for _ in range(0, retries): - eventlet.sleep(delay) + concurrency.sleep(delay) if mocked.call_count == expected_count: break @@ -831,7 +827,7 @@ def make_mock_stream_readline(mock_stream, mock_data, stop_counter=1, sleep_dela def mock_stream_readline(): if sleep_delay: - eventlet.sleep(sleep_delay) + concurrency.sleep(sleep_delay) if mock_stream.counter >= stop_counter: mock_stream.closed = True diff --git a/st2tests/st2tests/mocks/execution.py b/st2tests/st2tests/mocks/execution.py index 00e3c8ef11..d54defeb19 100644 --- a/st2tests/st2tests/mocks/execution.py +++ b/st2tests/st2tests/mocks/execution.py @@ -14,9 +14,10 @@ # limitations under the License. from __future__ import absolute_import -import eventlet import traceback +from st2common.util import concurrency + from st2actions.notifier import notifier from st2common.models.db.execution import ActionExecutionDB @@ -40,7 +41,7 @@ class MockExecutionPublisherNonBlocking(object): def publish_update(cls, payload): try: if isinstance(payload, ActionExecutionDB): - eventlet.spawn(notifier.get_notifier().process, payload) + concurrency.spawn(notifier.get_notifier().process, payload) except Exception: traceback.print_exc() print(payload) diff --git a/st2tests/st2tests/mocks/liveaction.py b/st2tests/st2tests/mocks/liveaction.py index 2b329e6b25..3a7a696d79 100644 --- a/st2tests/st2tests/mocks/liveaction.py +++ b/st2tests/st2tests/mocks/liveaction.py @@ -17,9 +17,10 @@ from __future__ import print_function import six -import eventlet import traceback +from st2common.util import concurrency + from st2actions import worker from st2actions.scheduler import entrypoint as scheduling from st2actions.scheduler import handler as scheduling_queue @@ -79,10 +80,10 @@ def publish_state(cls, payload, state): try: if isinstance(payload, LiveActionDB): if state == action_constants.LIVEACTION_STATUS_REQUESTED: - thread = eventlet.spawn(cls.process, payload) + thread = concurrency.spawn(cls.process, payload) cls.threads.append(thread) else: - thread = eventlet.spawn(worker.get_worker().process, payload) + thread = concurrency.spawn(worker.get_worker().process, payload) cls.threads.append(thread) except Exception: traceback.print_exc() @@ -92,13 +93,13 @@ def publish_state(cls, payload, state): def wait_all(cls): for thread in cls.threads: try: - thread.wait() + concurrency.wait(thread) except Exception as e: print(six.text_type(e)) finally: cls.threads.remove(thread) - eventlet.sleep(0.1) + concurrency.sleep(0.1) class MockLiveActionPublisherSchedulingQueueOnly(object): diff --git a/st2tests/st2tests/mocks/workflow.py b/st2tests/st2tests/mocks/workflow.py index 051bf5cb83..f9c8180df2 100644 --- a/st2tests/st2tests/mocks/workflow.py +++ b/st2tests/st2tests/mocks/workflow.py @@ -16,9 +16,10 @@ from __future__ import absolute_import import six -import eventlet import traceback +from st2common.util import concurrency + from st2actions.workflows import workflows from st2common.models.db import workflow as wf_ex_db @@ -53,7 +54,7 @@ class MockWorkflowExecutionPublisherNonBlocking(object): def publish_create(cls, payload): try: if isinstance(payload, wf_ex_db.WorkflowExecutionDB): - thread = eventlet.spawn(workflows.get_engine().process, payload) + thread = concurrency.spawn(workflows.get_engine().process, payload) cls.threads.append(thread) except Exception: traceback.print_exc() @@ -63,7 +64,7 @@ def publish_create(cls, payload): def publish_state(cls, payload, state): try: if isinstance(payload, wf_ex_db.WorkflowExecutionDB): - thread = eventlet.spawn(workflows.get_engine().process, payload) + thread = concurrency.spawn(workflows.get_engine().process, payload) cls.threads.append(thread) except Exception: traceback.print_exc() @@ -73,7 +74,7 @@ def publish_state(cls, payload, state): def wait_all(cls): for thread in cls.threads: try: - thread.wait() + concurrency.wait(thread) except Exception as e: print(six.text_type(e)) finally: From c7ad658d2c303da546e7f423ab93ec517669f4a4 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Thu, 26 Mar 2026 15:27:26 -0600 Subject: [PATCH 08/22] bump circleci version to 3.9 --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ac64873c98..a7487f03ec 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -43,7 +43,7 @@ jobs: # Run st2 Integration tests integration: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 - image: mongo:7.0 - image: rabbitmq:3.13 working_directory: ~/st2 @@ -80,7 +80,7 @@ jobs: # Run st2 Lint Checks lint: docker: - - image: circleci/python:3.8 + - image: circleci/python:3.9 - image: mongo:7.0 - image: rabbitmq:3.13 working_directory: ~/st2 @@ -114,7 +114,7 @@ jobs: resource_class: large docker: # The primary container is an instance of the first list image listed. Your build commands run in this container. - - image: circleci/python:3.8 + - image: circleci/python:3.9 working_directory: ~/st2 environment: - DISTROS: "focal jammy el8 el9" From 6e12760c79d683c63e72dab55f09b108c1a80ee5 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Mon, 20 Jul 2026 16:35:52 -0600 Subject: [PATCH 09/22] Compile/Lint fixes --- st2actions/st2actions/scheduler/handler.py | 4 +++- st2actions/st2actions/workflows/workflows.py | 4 +++- st2actions/tests/unit/test_scheduler_retry.py | 13 +++++++---- st2api/st2api/cmd/api.py | 1 + st2auth/st2auth/cmd/api.py | 1 + st2common/st2common/service_setup.py | 19 +++++++++++----- st2common/st2common/util/concurrency.py | 22 +++++++++++++++---- st2common/st2common/util/greenpooldispatch.py | 4 +++- st2common/st2common/util/monkey_patch.py | 3 ++- st2common/st2common/util/shell.py | 4 ++-- st2stream/st2stream/cmd/api.py | 2 ++ 11 files changed, 58 insertions(+), 19 deletions(-) diff --git a/st2actions/st2actions/scheduler/handler.py b/st2actions/st2actions/scheduler/handler.py index 636d0249da..2150768da0 100644 --- a/st2actions/st2actions/scheduler/handler.py +++ b/st2actions/st2actions/scheduler/handler.py @@ -51,7 +51,9 @@ class ActionExecutionSchedulingQueueHandler(object): def __init__(self): self.message_type = LiveActionDB self._shutdown = False - self._pool = concurrency.get_green_pool_class()(size=cfg.CONF.scheduler.pool_size) + self._pool = concurrency.get_green_pool_class()( + size=cfg.CONF.scheduler.pool_size + ) # If an ActionExecutionSchedulingQueueItemDB object hasn't been updated fore more than # this amount of milliseconds, it will be marked as "handled=False". # As soon as an item is picked by scheduler to be processed, it should be processed very diff --git a/st2actions/st2actions/workflows/workflows.py b/st2actions/st2actions/workflows/workflows.py index b8721e39fa..8384510f83 100644 --- a/st2actions/st2actions/workflows/workflows.py +++ b/st2actions/st2actions/workflows/workflows.py @@ -104,7 +104,9 @@ def process(self, message): self._active_messages -= 1 def start(self, wait): - concurrency.spawn_after(self._delay, self._resume_workflows_paused_during_shutdown) + concurrency.spawn_after( + self._delay, self._resume_workflows_paused_during_shutdown + ) super(WorkflowExecutionHandler, self).start(wait=wait) def shutdown(self): diff --git a/st2actions/tests/unit/test_scheduler_retry.py b/st2actions/tests/unit/test_scheduler_retry.py index cf5df656f2..26aed121d8 100644 --- a/st2actions/tests/unit/test_scheduler_retry.py +++ b/st2actions/tests/unit/test_scheduler_retry.py @@ -26,7 +26,6 @@ from st2common.models.db import execution_queue as ex_q_db from st2common.persistence import execution_queue as ex_q_db_access - __all__ = ["SchedulerHandlerRetryTestCase"] @@ -43,7 +42,9 @@ class SchedulerHandlerRetryTestCase(CleanDbTestCase): side_effect=[pymongo.errors.ConnectionFailure(), MOCK_QUEUE_ITEM] ), ) - @mock.patch.object(concurrency.get_green_pool_class(), "spawn", mock.MagicMock(return_value=None)) + @mock.patch.object( + concurrency.get_green_pool_class(), "spawn", mock.MagicMock(return_value=None) + ) def test_handler_retry_connection_error(self): scheduling_queue_handler = handler.ActionExecutionSchedulingQueueHandler() scheduling_queue_handler.process() @@ -57,7 +58,9 @@ def test_handler_retry_connection_error(self): "_get_next_execution", mock.MagicMock(side_effect=[pymongo.errors.ConnectionFailure()] * 3), ) - @mock.patch.object(concurrency.get_green_pool_class(), "spawn", mock.MagicMock(return_value=None)) + @mock.patch.object( + concurrency.get_green_pool_class(), "spawn", mock.MagicMock(return_value=None) + ) def test_handler_retries_exhausted(self): scheduling_queue_handler = handler.ActionExecutionSchedulingQueueHandler() self.assertRaises( @@ -70,7 +73,9 @@ def test_handler_retries_exhausted(self): "_get_next_execution", mock.MagicMock(side_effect=KeyError()), ) - @mock.patch.object(concurrency.get_green_pool_class(), "spawn", mock.MagicMock(return_value=None)) + @mock.patch.object( + concurrency.get_green_pool_class(), "spawn", mock.MagicMock(return_value=None) + ) def test_handler_retry_unexpected_error(self): scheduling_queue_handler = handler.ActionExecutionSchedulingQueueHandler() self.assertRaises(KeyError, scheduling_queue_handler.process) diff --git a/st2api/st2api/cmd/api.py b/st2api/st2api/cmd/api.py index 04bf85b526..1b640e0347 100644 --- a/st2api/st2api/cmd/api.py +++ b/st2api/st2api/cmd/api.py @@ -18,6 +18,7 @@ # See https://github.com/StackStorm/st2/issues/4832 and https://github.com/gevent/gevent/issues/1016 # for details. from st2common.util.monkey_patch import monkey_patch + monkey_patch() import os diff --git a/st2auth/st2auth/cmd/api.py b/st2auth/st2auth/cmd/api.py index 125ec6c305..27554efb64 100644 --- a/st2auth/st2auth/cmd/api.py +++ b/st2auth/st2auth/cmd/api.py @@ -14,6 +14,7 @@ # limitations under the License. from st2common.util.monkey_patch import monkey_patch + monkey_patch() import os diff --git a/st2common/st2common/service_setup.py b/st2common/st2common/service_setup.py index 3e91ca7aee..c8fb4527d0 100644 --- a/st2common/st2common/service_setup.py +++ b/st2common/st2common/service_setup.py @@ -183,7 +183,8 @@ def setup( or encoding.lower() not in VALID_UTF8_ENCODINGS ): LOG.warning( - NON_UTF8_LOCALE_WARNING_MSG % (fs_encoding, default_encoding, used_locale.strip()) + NON_UTF8_LOCALE_WARNING_MSG + % (fs_encoding, default_encoding, used_locale.strip()) ) is_debug_enabled = cfg.CONF.debug or cfg.CONF.system.debug @@ -197,7 +198,9 @@ def setup( except KeyError as e: tb_msg = traceback.format_exc() if "log.setLevel" in tb_msg: - msg = "Invalid log level selected. Log level names need to be all uppercase." + msg = ( + "Invalid log level selected. Log level names need to be all uppercase." + ) msg += "\n\n" + getattr(e, "message", six.text_type(e)) raise KeyError(msg) else: @@ -212,7 +215,8 @@ def setup( # set to "INFO" and we already log messages with level AUDIT to a special dedicated log # file. ignore_audit_log_messages = ( - handler.level >= stdlib_logging.INFO and handler.level < stdlib_logging.AUDIT + handler.level >= stdlib_logging.INFO + and handler.level < stdlib_logging.AUDIT ) if not is_debug_enabled and ignore_audit_log_messages: try: @@ -221,7 +225,10 @@ def setup( # In case handler doesn't have name assigned, repr would throw handler_repr = "unknown" - LOG.debug('Excluding log messages with level "AUDIT" for handler "%s"' % (handler_repr)) + LOG.debug( + 'Excluding log messages with level "AUDIT" for handler "%s"' + % (handler_repr) + ) handler.addFilter(LogLevelFilter(log_levels=exclude_log_levels)) if not is_debug_enabled: @@ -347,7 +354,9 @@ def deregister_service(service, start_heart=True): coordinator = coordination.get_coordinator(start_heart=start_heart) member_id = coordination.get_member_id() - LOG.debug('Leaving service registry group "%s" as member_id "%s"' % (group_id, member_id)) + LOG.debug( + 'Leaving service registry group "%s" as member_id "%s"' % (group_id, member_id) + ) try: coordinator.leave_group(group_id).get() except (GroupNotCreated, MemberNotJoined): diff --git a/st2common/st2common/util/concurrency.py b/st2common/st2common/util/concurrency.py index c4b1bccbb6..0111227f41 100644 --- a/st2common/st2common/util/concurrency.py +++ b/st2common/st2common/util/concurrency.py @@ -80,11 +80,22 @@ def get_subprocess_module(): else: raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") -def wsgi_server(socket, app, custom_pool=None, log=None, log_output=True, *args, **kwargs): + +def wsgi_server( + socket, app, custom_pool=None, log=None, log_output=True, *args, **kwargs +): if CONCURRENCY_LIBRARY == "eventlet": from eventlet import wsgi - wsgi.server(socket, app, custom_pool=custom_pool, log=log, log_output=log_output, *args, **kwargs) + wsgi.server( + socket, + app, + custom_pool=custom_pool, + log=log, + log_output=log_output, + *args, + **kwargs, + ) elif CONCURRENCY_LIBRARY == "gevent": from gevent import pywsgi @@ -93,6 +104,7 @@ def wsgi_server(socket, app, custom_pool=None, log=None, log_output=True, *args, else: raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + def subprocess_popen(*args, **kwargs): if CONCURRENCY_LIBRARY == "eventlet": from eventlet.green import subprocess # pylint: disable=import-error @@ -294,9 +306,10 @@ def listen_server(host, port, backlog=50, **kwargs): else: raise ValueError("Unsupported concurrency library") + def wrap_ssl(socket, *args, **kwargs): if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.wrap_socket(socket, *args, **kwargs) + return eventlet.wrap_ssl(socket, *args, **kwargs) elif CONCURRENCY_LIBRARY == "gevent": # Monkey patching in the caller module is required prior to # calling wrap_ssl() or this may block. @@ -306,12 +319,13 @@ def wrap_ssl(socket, *args, **kwargs): else: raise ValueError("Unsupported concurrency library") + def blocking_detection(enable=False, timeout=1.0): if CONCURRENCY_LIBRARY == "eventlet": print( f"Eventlet long running / blocking operation detection logic enabled. Block timeout ({timeout})." ) - eventlet.debug.hub_blocking_detection(enable_detection=enable, resolution=timeout) + eventlet.debug.hub_blocking_detection(state=enable, resolution=timeout) elif CONCURRENCY_LIBRARY == "gevent": print( f"gEvent long running / blocking operation detection logic enabled. Block timeout ({timeout})." diff --git a/st2common/st2common/util/greenpooldispatch.py b/st2common/st2common/util/greenpooldispatch.py index 61dff7274c..4ad5eab872 100644 --- a/st2common/st2common/util/greenpooldispatch.py +++ b/st2common/st2common/util/greenpooldispatch.py @@ -91,7 +91,9 @@ def _flush_now(self): # Update the time of when there were free threads available self._pool_last_free_ts = time.time() - while not self._work_buffer.empty() and concurrency.is_green_pool_free(self._dispatcher_pool): + while not self._work_buffer.empty() and concurrency.is_green_pool_free( + self._dispatcher_pool + ): (handler, args) = self._work_buffer.get_nowait() self._dispatcher_pool.spawn(handler, *args) diff --git a/st2common/st2common/util/monkey_patch.py b/st2common/st2common/util/monkey_patch.py index c852a48b2c..79c63582de 100644 --- a/st2common/st2common/util/monkey_patch.py +++ b/st2common/st2common/util/monkey_patch.py @@ -57,8 +57,9 @@ def monkey_patch(patch_thread=None): os=True, select=True, socket=True, thread=patch_thread, time=True ) elif concurrency_library == "gevent": - # Match what eventlet was enabling, fallback on gevent defaults + # Match what eventlet was enabling, fallback on gevent defaults import gevent + gevent.monkey.patch_all( os=True, select=True, thread=True, time=True, socket=True ) diff --git a/st2common/st2common/util/shell.py b/st2common/st2common/util/shell.py index 171085be74..7259f180ad 100644 --- a/st2common/st2common/util/shell.py +++ b/st2common/st2common/util/shell.py @@ -124,9 +124,9 @@ def kill_process(process): try: if six.PY3: - status = subprocess.call( + status = subprocess.call( # pylint: disable=not-callable kill_command, timeout=100 - ) # pylint: disable=not-callable + ) else: status = subprocess.call(kill_command) # pylint: disable=not-callable except Exception: diff --git a/st2stream/st2stream/cmd/api.py b/st2stream/st2stream/cmd/api.py index 9658085818..cbc3cd11d7 100644 --- a/st2stream/st2stream/cmd/api.py +++ b/st2stream/st2stream/cmd/api.py @@ -14,6 +14,7 @@ # limitations under the License. from st2common.util.monkey_patch import monkey_patch + monkey_patch() import os @@ -64,6 +65,7 @@ def _setup(): capabilities=capabilities, ) + def _run_server(): host = cfg.CONF.stream.host From 2ca0d53df27df375974f5360792b5929927b6440 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Wed, 22 Jul 2026 12:08:14 -0600 Subject: [PATCH 10/22] compile/lint updates --- Makefile | 4 ++-- st2api/tests/integration/test_gunicorn_configs.py | 11 ++++++----- st2common/st2common/util/concurrency.py | 11 ++++++++++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 45043bc352..e964c91765 100644 --- a/Makefile +++ b/Makefile @@ -250,7 +250,7 @@ check-python-packages: @echo "================== CHECK PYTHON PACKAGES ====================" @echo "" test -f $(VIRTUALENV_COMPONENTS_DIR)/bin/activate || $(PYBIN) -m venv $(VIRTUALENV_COMPONENTS_DIR) --system-site-packages - $(VIRTUALENV_COMPONENTS_DIR)/bin/pip install --quiet "setuptools==$(SETUPTOOLS_VERSION)" + $(VIRTUALENV_COMPONENTS_DIR)/bin/pip install --quiet "setuptools==$(SETUPTOOLS_VERSION)" setuptools_scm @for component in $(COMPONENTS_WITHOUT_ST2TESTS); do \ echo "==========================================================="; \ echo "Checking component:" $$component; \ @@ -267,7 +267,7 @@ check-python-packages-nightly: @echo "" test -f $(VIRTUALENV_COMPONENTS_DIR)/bin/activate || $(PYBIN) -m venv $(VIRTUALENV_COMPONENTS_DIR) --system-site-packages - $(VIRTUALENV_COMPONENTS_DIR)/bin/pip install --quiet "setuptools==$(SETUPTOOLS_VERSION)" wheel + $(VIRTUALENV_COMPONENTS_DIR)/bin/pip install --quiet "setuptools==$(SETUPTOOLS_VERSION)" setuptools_scm wheel @for component in $(COMPONENTS_WITHOUT_ST2TESTS); do \ echo "==========================================================="; \ echo "Checking component:" $$component; \ diff --git a/st2api/tests/integration/test_gunicorn_configs.py b/st2api/tests/integration/test_gunicorn_configs.py index 0ca3fee2c1..19acc4a131 100644 --- a/st2api/tests/integration/test_gunicorn_configs.py +++ b/st2api/tests/integration/test_gunicorn_configs.py @@ -22,14 +22,11 @@ from st2common.util import concurrency -subprocess = concurrency.get_subprocess_module() - import st2tests.config from st2common.models.utils import profiling from st2common.util.shell import kill_process from st2tests.base import IntegrationTestCase - BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ST2_CONFIG_PATH = os.path.join(BASE_DIR, "../../../conf/st2.tests.conf") @@ -47,7 +44,9 @@ def test_st2api_wsgi_entry_point(self): env.update(st2tests.config.db_opts_as_env_vars()) env.update(st2tests.config.mq_opts_as_env_vars()) env.update(st2tests.config.coord_opts_as_env_vars()) - process = subprocess.Popen(cmd, env=env, shell=True, preexec_fn=os.setsid) + process = concurrency.subprocess_popen( + cmd, env=env, shell=True, preexec_fn=os.setsid + ) try: self.add_process(process=process) concurrency.sleep(8) @@ -69,7 +68,9 @@ def test_st2auth(self): env.update(st2tests.config.db_opts_as_env_vars()) env.update(st2tests.config.mq_opts_as_env_vars()) env.update(st2tests.config.coord_opts_as_env_vars()) - process = subprocess.Popen(cmd, env=env, shell=True, preexec_fn=os.setsid) + process = concurrency.subprocess_popen( + cmd, env=env, shell=True, preexec_fn=os.setsid + ) try: self.add_process(process=process) concurrency.sleep(8) diff --git a/st2common/st2common/util/concurrency.py b/st2common/st2common/util/concurrency.py index 0111227f41..a668ae8d2e 100644 --- a/st2common/st2common/util/concurrency.py +++ b/st2common/st2common/util/concurrency.py @@ -315,7 +315,16 @@ def wrap_ssl(socket, *args, **kwargs): # calling wrap_ssl() or this may block. import ssl - return ssl.wrap_socket(socket, *args, **kwargs) + server_side = kwargs.pop("server_side", False) + certfile = kwargs.pop("certfile", None) + keyfile = kwargs.pop("keyfile", None) + + protocol = ssl.PROTOCOL_TLS_SERVER if server_side else ssl.PROTOCOL_TLS_CLIENT + context = ssl.SSLContext(protocol) + if certfile: + context.load_cert_chain(certfile, keyfile) + + return context.wrap_socket(socket, *args, server_side=server_side, **kwargs) else: raise ValueError("Unsupported concurrency library") From 7bcd1b7b7f9fe50e2b30fb8f9796c57fe942c901 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Wed, 12 Aug 2026 14:52:35 -0600 Subject: [PATCH 11/22] feat(concurrency): make gevent a real drop-in replacement for eventlet - Read concurrency_library from st2.conf [system] instead of an env var, defaulting to gevent. - Pick the gunicorn worker class (-k eventlet/-k gevent) dynamically based on the configured concurrency library instead of hardcoding eventlet. - Fix real gevent bugs: wrap_ssl() using the removed ssl.wrap_socket(), green_pool_wait_all() not actually blocking, wait() not returning the greenlet's result, and several call sites bypassing the concurrency wrapper with direct .wait()/.free() calls on greenlets/pools. - Fix a gevent-only hang in the python runner: process.stdin was written to but never closed/flushed for large (>64KB) parameters, leaving the child process blocked forever waiting for input that never arrived. - Remove remaining direct eventlet imports outside the concurrency compatibility layer (tools, examples, test mocks); keep back-compat aliases (EventletTestCase, setup_eventlet_profiler, blocking_eventlet_spawn) for external callers. --- conf/st2.conf.sample | 3 + .../examples/actions/python-mock-create-vm.py | 4 +- contrib/hello_st2/sensors/sensor1.py | 5 +- .../tests/unit/test_actionchain.py | 2 +- .../tests/integration/test_localrunner.py | 8 +-- .../tests/unit/test_pythonrunner.py | 6 +- st2actions/st2actions/scheduler/handler.py | 2 +- .../tests/unit/policies/test_concurrency.py | 6 +- .../unit/policies/test_concurrency_by_attr.py | 6 +- st2actions/tests/unit/test_worker.py | 8 +-- .../integration/test_gunicorn_configs.py | 13 ++-- st2common/st2common/config.py | 9 +++ st2common/st2common/runners/parallel_ssh.py | 2 +- st2common/st2common/service_setup.py | 4 +- st2common/st2common/transport/consumers.py | 2 +- st2common/st2common/util/concurrency.py | 62 +++++++++++++++++-- st2common/st2common/util/green/shell.py | 2 + st2common/st2common/util/profiler.py | 28 ++++----- st2reactor/st2reactor/cmd/timersengine.py | 2 +- st2reactor/st2reactor/container/manager.py | 2 +- st2reactor/tests/unit/test_timer.py | 3 +- .../integration/orquesta/test_performance.py | 4 +- .../orquesta/test_wiring_error_handling.py | 4 +- .../orquesta/test_wiring_inquiry.py | 5 +- st2tests/st2tests/__init__.py | 2 + st2tests/st2tests/base.py | 25 +++++--- tools/launchdev.sh | 15 ++++- tools/queue_producer.py | 4 +- tools/st2-inject-trigger-instances.py | 10 +-- 29 files changed, 166 insertions(+), 82 deletions(-) diff --git a/conf/st2.conf.sample b/conf/st2.conf.sample index 9dfcc34fc8..cfba95c91e 100644 --- a/conf/st2.conf.sample +++ b/conf/st2.conf.sample @@ -341,6 +341,9 @@ protocol = udp [system] # Base path to all st2 artifacts. base_path = /opt/stackstorm +# Green thread concurrency library to use ('eventlet' or 'gevent'). NOTE: This is read directly from this config file at process startup, before oslo_config is available, so it cannot be overridden via CLI args or environment variables. +# Valid values: eventlet, gevent +concurrency_library = gevent # Enable debug mode. debug = False # True to validate action and runner output against schema. diff --git a/contrib/examples/actions/python-mock-create-vm.py b/contrib/examples/actions/python-mock-create-vm.py index 788a7d11df..3cfb50b4e5 100644 --- a/contrib/examples/actions/python-mock-create-vm.py +++ b/contrib/examples/actions/python-mock-create-vm.py @@ -13,15 +13,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -import eventlet import random from st2common.runners.base_action import Action +from st2common.util import concurrency class MockCreateVMAction(Action): def run(self, cpu_cores, memory_mb, vm_name, ip): - eventlet.sleep(5) + concurrency.sleep(5) data = { "vm_id": "vm" + str(random.randint(0, 10000)), diff --git a/contrib/hello_st2/sensors/sensor1.py b/contrib/hello_st2/sensors/sensor1.py index 1bc534bf69..ba119ad720 100644 --- a/contrib/hello_st2/sensors/sensor1.py +++ b/contrib/hello_st2/sensors/sensor1.py @@ -13,8 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import eventlet - +from st2common.util import concurrency from st2reactor.sensor.base import Sensor @@ -34,7 +33,7 @@ def run(self): payload = {"greeting": "Yo, StackStorm!", "count": int(count) + 1} self.sensor_service.dispatch(trigger="hello_st2.event1", payload=payload) self.sensor_service.set_value("hello_st2.count", payload["count"]) - eventlet.sleep(60) + concurrency.sleep(60) def cleanup(self): self._stop = True diff --git a/contrib/runners/action_chain_runner/tests/unit/test_actionchain.py b/contrib/runners/action_chain_runner/tests/unit/test_actionchain.py index 953b2d6acb..ffd3aae35b 100644 --- a/contrib/runners/action_chain_runner/tests/unit/test_actionchain.py +++ b/contrib/runners/action_chain_runner/tests/unit/test_actionchain.py @@ -368,7 +368,7 @@ def test_chain_runner_bad_default(self, request): runnerexceptions.ActionRunnerPreRunError, expected_msg, chain_runner.pre_run ) - @mock.patch("eventlet.sleep", mock.MagicMock()) + @mock.patch("st2common.util.concurrency.sleep", mock.MagicMock()) @mock.patch.object( action_db_util, "get_liveaction_by_id", diff --git a/contrib/runners/local_runner/tests/integration/test_localrunner.py b/contrib/runners/local_runner/tests/integration/test_localrunner.py index f165a72ff3..68cf6a303d 100644 --- a/contrib/runners/local_runner/tests/integration/test_localrunner.py +++ b/contrib/runners/local_runner/tests/integration/test_localrunner.py @@ -35,7 +35,7 @@ from st2common.constants.runners import LOCAL_RUNNER_DEFAULT_ACTION_TIMEOUT from st2tests.base import RunnerTestCase from st2tests.base import CleanDbTestCase -from st2tests.base import blocking_eventlet_spawn +from st2tests.base import blocking_concurrency_spawn from st2tests.base import make_mock_stream_readline from st2tests.fixtures.generic.fixture import PACK_NAME as GENERIC_PACK from st2tests.fixtures.localrunner_pack.fixture import PACK_NAME as LOCALRUNNER_PACK @@ -181,7 +181,7 @@ def test_action_stdout_and_stderr_is_stored_in_the_db(self, mock_spawn, mock_pop # Note: We need to mock spawn function so we can test everything in single event loop # iteration - mock_spawn.side_effect = blocking_eventlet_spawn + mock_spawn.side_effect = blocking_concurrency_spawn # No output to stdout and no result (implicit None) mock_stdout = [ @@ -249,7 +249,7 @@ def test_action_stdout_and_stderr_is_stored_in_the_db_short_running_action( # Note: We need to mock spawn function so we can test everything in single event loop # iteration - mock_spawn.side_effect = blocking_eventlet_spawn + mock_spawn.side_effect = blocking_concurrency_spawn # No output to stdout and no result (implicit None) mock_stdout = ["stdout line 1\n", "stdout line 2\n"] @@ -553,7 +553,7 @@ def test_action_stdout_and_stderr_is_stored_in_the_db(self, mock_spawn, mock_pop # Note: We need to mock spawn function so we can test everything in single event loop # iteration - mock_spawn.side_effect = blocking_eventlet_spawn + mock_spawn.side_effect = blocking_concurrency_spawn # No output to stdout and no result (implicit None) mock_stdout = [ diff --git a/contrib/runners/python_runner/tests/unit/test_pythonrunner.py b/contrib/runners/python_runner/tests/unit/test_pythonrunner.py index 22d9e14b36..3136538c42 100644 --- a/contrib/runners/python_runner/tests/unit/test_pythonrunner.py +++ b/contrib/runners/python_runner/tests/unit/test_pythonrunner.py @@ -41,7 +41,7 @@ from python_runner.python_action_wrapper import PythonActionWrapper from st2tests.base import RunnerTestCase from st2tests.base import CleanDbTestCase -from st2tests.base import blocking_eventlet_spawn +from st2tests.base import blocking_concurrency_spawn from st2tests.base import make_mock_stream_readline from st2tests.fixtures.packs.dummy_pack_1.fixture import PACK_NAME as DUMMY_PACK_1 from st2tests.fixtures.packs.dummy_pack_5.fixture import PACK_NAME as DUMMY_PACK_5 @@ -321,7 +321,7 @@ def test_action_stdout_and_stderr_is_not_stored_in_db_by_default( # Note: We need to mock spawn function so we can test everything in single event loop # iteration - mock_spawn.side_effect = blocking_eventlet_spawn + mock_spawn.side_effect = blocking_concurrency_spawn # No output to stdout and no result (implicit None) mock_stdout = [ @@ -402,7 +402,7 @@ def test_action_stdout_and_stderr_is_stored_in_the_db(self, mock_spawn, mock_pop # Note: We need to mock spawn function so we can test everything in single event loop # iteration - mock_spawn.side_effect = blocking_eventlet_spawn + mock_spawn.side_effect = blocking_concurrency_spawn # No output to stdout and no result (implicit None) mock_stdout = [ diff --git a/st2actions/st2actions/scheduler/handler.py b/st2actions/st2actions/scheduler/handler.py index 2150768da0..7874563220 100644 --- a/st2actions/st2actions/scheduler/handler.py +++ b/st2actions/st2actions/scheduler/handler.py @@ -514,7 +514,7 @@ def shutdown(self, *args, **kwargs): def wait(self): # Wait for the worker threads to complete. If there is an exception thrown in the thread, # then the exception will be propagated to the main process for a proper return code. - self._main_thread.wait() or self._cleanup_thread.wait() + concurrency.wait(self._main_thread) or concurrency.wait(self._cleanup_thread) def get_handler(): diff --git a/st2actions/tests/unit/policies/test_concurrency.py b/st2actions/tests/unit/policies/test_concurrency.py index 7612bd5396..e261d67cff 100644 --- a/st2actions/tests/unit/policies/test_concurrency.py +++ b/st2actions/tests/unit/policies/test_concurrency.py @@ -21,7 +21,7 @@ # This import must be early for import-time side-effects. # Importing st2actions.scheduler relies on config being parsed :/ -from st2tests import DbTestCase, EventletTestCase, ExecutionDbTestCase +from st2tests import DbTestCase, GreenThreadTestCase, ExecutionDbTestCase import st2common from st2actions.scheduler import handler as scheduling_queue @@ -78,10 +78,10 @@ mock.MagicMock(side_effect=MockExecutionPublisher.publish_update), ) @mock.patch.object(CUDPublisher, "publish_create", mock.MagicMock(return_value=None)) -class ConcurrencyPolicyTestCase(EventletTestCase, ExecutionDbTestCase): +class ConcurrencyPolicyTestCase(GreenThreadTestCase, ExecutionDbTestCase): @classmethod def setUpClass(cls): - EventletTestCase.setUpClass() + GreenThreadTestCase.setUpClass() DbTestCase.setUpClass() # Override the coordinator to use the noop driver otherwise the tests will be blocked. diff --git a/st2actions/tests/unit/policies/test_concurrency_by_attr.py b/st2actions/tests/unit/policies/test_concurrency_by_attr.py index 2edcdb4af7..1eb1f80b9b 100644 --- a/st2actions/tests/unit/policies/test_concurrency_by_attr.py +++ b/st2actions/tests/unit/policies/test_concurrency_by_attr.py @@ -20,7 +20,7 @@ # This import must be early for import-time side-effects. # Importing st2actions.scheduler relies on config being parsed :/ -from st2tests import ExecutionDbTestCase, EventletTestCase +from st2tests import ExecutionDbTestCase, GreenThreadTestCase import st2common from st2actions.scheduler import handler as scheduling_queue @@ -76,10 +76,10 @@ mock.MagicMock(side_effect=MockExecutionPublisher.publish_update), ) @mock.patch.object(CUDPublisher, "publish_create", mock.MagicMock(return_value=None)) -class ConcurrencyByAttributePolicyTestCase(EventletTestCase, ExecutionDbTestCase): +class ConcurrencyByAttributePolicyTestCase(GreenThreadTestCase, ExecutionDbTestCase): @classmethod def setUpClass(cls): - EventletTestCase.setUpClass() + GreenThreadTestCase.setUpClass() ExecutionDbTestCase.setUpClass() # Override the coordinator to use the noop driver otherwise the tests will be blocked. diff --git a/st2actions/tests/unit/test_worker.py b/st2actions/tests/unit/test_worker.py index 9480064244..3041c90a99 100644 --- a/st2actions/tests/unit/test_worker.py +++ b/st2actions/tests/unit/test_worker.py @@ -197,7 +197,7 @@ def test_worker_shutdown(self): # Wait for the local runner to complete. This will activate the finally block in # _run_action but will not result in KeyError because the discard method is used to # to remove the liveaction from _running_liveactions. - runner_thread.wait() + concurrency.wait(runner_thread) @mock.patch.object( RedisDriver, @@ -266,7 +266,7 @@ def test_worker_graceful_shutdown_with_multiple_runners(self): # Wait for the local runner to complete. This will activate the finally block in # _run_action but will not result in KeyError because the discard method is used to # to remove the liveaction from _running_liveactions. - runner_thread.wait() + concurrency.wait(runner_thread) shutdown_thread.kill() def test_worker_graceful_shutdown_with_single_runner(self): @@ -331,7 +331,7 @@ def test_worker_graceful_shutdown_with_single_runner(self): # Wait for the local runner to complete. This will activate the finally block in # _run_action but will not result in KeyError because the discard method is used to # to remove the liveaction from _running_liveactions. - runner_thread.wait() + concurrency.wait(runner_thread) shutdown_thread.kill() @mock.patch.object( @@ -397,5 +397,5 @@ def test_worker_graceful_shutdown_exit_timeout(self): # Wait for the local runner to complete. This will activate the finally block in # _run_action but will not result in KeyError because the discard method is used to # to remove the liveaction from _running_liveactions. - runner_thread.wait() + concurrency.wait(runner_thread) shutdown_thread.kill() diff --git a/st2api/tests/integration/test_gunicorn_configs.py b/st2api/tests/integration/test_gunicorn_configs.py index 19acc4a131..eff77b695e 100644 --- a/st2api/tests/integration/test_gunicorn_configs.py +++ b/st2api/tests/integration/test_gunicorn_configs.py @@ -30,14 +30,19 @@ BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ST2_CONFIG_PATH = os.path.join(BASE_DIR, "../../../conf/st2.tests.conf") +# Gunicorn's worker class must match the concurrency library the spawned process will +# use (per ST2_CONFIG_PATH above), or the WSGI worker hangs: eventlet/gevent monkey +# patching under the other library's gunicorn worker doesn't work. +GUNICORN_WORKER_CLASS = concurrency._get_concurrency_library_from_conf(ST2_CONFIG_PATH) + class GunicornWSGIEntryPointTestCase(IntegrationTestCase): @pytest.mark.skipif(profiling.is_enabled(), reason="Profiling is enabled") def test_st2api_wsgi_entry_point(self): port = random.randint(10000, 30000) cmd = ( - 'gunicorn st2api.wsgi:application -k eventlet -b "127.0.0.1:%s" --workers 1' - % port + 'gunicorn st2api.wsgi:application -k %s -b "127.0.0.1:%s" --workers 1' + % (GUNICORN_WORKER_CLASS, port) ) env = os.environ.copy() env["ST2_CONFIG_PATH"] = ST2_CONFIG_PATH @@ -60,8 +65,8 @@ def test_st2api_wsgi_entry_point(self): def test_st2auth(self): port = random.randint(10000, 30000) cmd = ( - 'gunicorn st2auth.wsgi:application -k eventlet -b "127.0.0.1:%s" --workers 1' - % port + 'gunicorn st2auth.wsgi:application -k %s -b "127.0.0.1:%s" --workers 1' + % (GUNICORN_WORKER_CLASS, port) ) env = os.environ.copy() env["ST2_CONFIG_PATH"] = ST2_CONFIG_PATH diff --git a/st2common/st2common/config.py b/st2common/st2common/config.py index 53b5f737c1..d62d68fb4d 100644 --- a/st2common/st2common/config.py +++ b/st2common/st2common/config.py @@ -123,6 +123,15 @@ def register_opts(ignore_errors=False): default=False, help="True to validate action and runner output against schema.", ), + cfg.StrOpt( + "concurrency_library", + default="gevent", + choices=["eventlet", "gevent"], + help="Green thread concurrency library to use ('eventlet' or 'gevent'). " + "NOTE: This is read directly from this config file at process startup, " + "before oslo_config is available, so it cannot be overridden via CLI args " + "or environment variables.", + ), ] do_register_opts(system_opts, "system", ignore_errors) diff --git a/st2common/st2common/runners/parallel_ssh.py b/st2common/st2common/runners/parallel_ssh.py index 9aee991b0c..bcf4f8f086 100644 --- a/st2common/st2common/runners/parallel_ssh.py +++ b/st2common/st2common/runners/parallel_ssh.py @@ -242,7 +242,7 @@ def _execute_in_pool(self, execute_method, **kwargs): results[host] = self._bad_hosts[host] for host in self._hosts_client.keys(): - while not self._pool.free(): + while not concurrency_lib.is_green_pool_free(self._pool): concurrency_lib.sleep(self._scan_interval) self._pool.spawn(execute_method, host=host, results=results, **kwargs) diff --git a/st2common/st2common/service_setup.py b/st2common/st2common/service_setup.py index c8fb4527d0..ff3b1fb350 100644 --- a/st2common/st2common/service_setup.py +++ b/st2common/st2common/service_setup.py @@ -49,7 +49,7 @@ from st2common.logging.misc import add_global_filters_for_all_loggers from st2common.constants.error_messages import PYTHON2_DEPRECATION from st2common.services.coordination import get_driver_name -from st2common.util.profiler import setup_eventlet_profiler +from st2common.util.profiler import setup_green_profiler # Note: This is here for backward compatibility. # Function has been moved in a standalone module to avoid expensive in-direct @@ -128,7 +128,7 @@ def setup( config.parse_args() if cfg.CONF.enable_profiler: - setup_eventlet_profiler(service_name="st2" + service) + setup_green_profiler(service_name="st2" + service) version = "%s.%s.%s" % ( sys.version_info[0], diff --git a/st2common/st2common/transport/consumers.py b/st2common/st2common/transport/consumers.py index 6f4cca7c87..853826c7c6 100644 --- a/st2common/st2common/transport/consumers.py +++ b/st2common/st2common/transport/consumers.py @@ -200,7 +200,7 @@ def start(self, wait=False): self.wait() def wait(self): - self._consumer_thread.wait() + return concurrency.wait(self._consumer_thread) def shutdown(self): LOG.info("Shutting down %s...", self.__class__.__name__) diff --git a/st2common/st2common/util/concurrency.py b/st2common/st2common/util/concurrency.py index a668ae8d2e..0d21aa5230 100644 --- a/st2common/st2common/util/concurrency.py +++ b/st2common/st2common/util/concurrency.py @@ -20,7 +20,10 @@ "set_concurrency_library" function. """ -import os +import configparser +import cProfile + +from st2common.constants.system import DEFAULT_CONFIG_FILE_PATH try: import eventlet # pylint: disable=import-error @@ -30,12 +33,27 @@ try: import gevent # pylint: disable=import-error # pants: no-infer-dep import gevent.lock + import gevent.monkey import gevent.pool import gevent.queue except ImportError: gevent = None -CONCURRENCY_LIBRARY = os.environ.get("ST2_CONCURRENCY_LIBRARY", "eventlet") + +def _get_concurrency_library_from_conf(config_path=DEFAULT_CONFIG_FILE_PATH): + # NOTE: We can't use st2common.config/oslo_config here because eventlet/gevent + # monkey patching (see st2common.util.monkey_patch) must happen before almost + # anything else is imported, which is earlier than oslo_config can be parsed. + # This minimal read only depends on the stdlib. + parser = configparser.ConfigParser() + parser.read(config_path) # no-op if the file doesn't exist + try: + return parser.get("system", "concurrency_library") + except (configparser.NoSectionError, configparser.NoOptionError): + return "gevent" + + +CONCURRENCY_LIBRARY = _get_concurrency_library_from_conf() __all__ = [ "set_concurrency_library", @@ -51,6 +69,7 @@ "get_green_pool_class", "is_green_pool_free", "green_pool_wait_all", + "get_green_profiler", ] @@ -149,7 +168,10 @@ def wait(green_thread, *args, **kwargs): if CONCURRENCY_LIBRARY == "eventlet": return green_thread.wait(*args, **kwargs) elif CONCURRENCY_LIBRARY == "gevent": - return green_thread.join(*args, **kwargs) + # NOTE: Greenlet.join() blocks but always returns None; .get() blocks + # and returns the greenlet's result (or re-raises its exception), + # matching eventlet's GreenThread.wait() semantics. + return green_thread.get(*args, **kwargs) else: raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") @@ -281,9 +303,7 @@ def green_pool_wait_all(pool): if CONCURRENCY_LIBRARY == "eventlet": return pool.waitall() elif CONCURRENCY_LIBRARY == "gevent": - # NOTE: This mimicks eventlet.waitall() functionality better than - # pool.join() - return all(gl.ready() for gl in pool.greenlets) + return pool.join() else: raise ValueError("Unsupported concurrency library") @@ -329,6 +349,36 @@ def wrap_ssl(socket, *args, **kwargs): raise ValueError("Unsupported concurrency library") +def get_green_profiler(): + """ + Return a (profiler, start, stop) tuple for a green-thread-aware profiler. + + Only to be used with eventlet/gevent code (aka a StackStorm service minus the CLI). + """ + if CONCURRENCY_LIBRARY == "eventlet": + if not eventlet.patcher.is_monkey_patched("os"): + raise ValueError( + "No eventlet monkey patching detected. Code may not be using eventlet" + ) + + from eventlet.green import profile + + profiler = profile.Profile() + return profiler, profiler.start, profiler.stop + elif CONCURRENCY_LIBRARY == "gevent": + if not gevent.monkey.is_module_patched("os"): + raise ValueError( + "No gevent monkey patching detected. Code may not be using gevent" + ) + + # gevent doesn't ship a greenlet-aware profile module; regular cProfile works fine + # since gevent greenlets are cooperative and run on a single OS thread. + profiler = cProfile.Profile() + return profiler, profiler.enable, profiler.disable + else: + raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + + def blocking_detection(enable=False, timeout=1.0): if CONCURRENCY_LIBRARY == "eventlet": print( diff --git a/st2common/st2common/util/green/shell.py b/st2common/st2common/util/green/shell.py index 64830c9423..3aef24432c 100644 --- a/st2common/st2common/util/green/shell.py +++ b/st2common/st2common/util/green/shell.py @@ -204,6 +204,8 @@ def on_timeout_expired(timeout): stdin_value = stdin_value.encode("utf-8") process.stdin.write(stdin_value) + # Without closing, large writes can hang under gevent (child never sees the data). + process.stdin.close() if read_stdout_func and read_stderr_func: LOG.debug("Using real-time stdout and stderr read mode, calling process.wait()") diff --git a/st2common/st2common/util/profiler.py b/st2common/st2common/util/profiler.py index b8760a2046..49c866d384 100644 --- a/st2common/st2common/util/profiler.py +++ b/st2common/st2common/util/profiler.py @@ -19,10 +19,9 @@ import platform import cProfile -import eventlet -from eventlet.green import profile +from st2common.util import concurrency -__all__ = ["setup_regular_profiler", "setup_eventlet_profiler"] +__all__ = ["setup_regular_profiler", "setup_green_profiler", "setup_eventlet_profiler"] def setup_regular_profiler(service_name: str) -> None: @@ -49,20 +48,14 @@ def stop_profiler(): atexit.register(stop_profiler) -def setup_eventlet_profiler(service_name: str) -> None: +def setup_green_profiler(service_name: str) -> None: """ - Set up eventlet profiler and write results to a file on exit. + Set up a green-thread (eventlet/gevent) aware profiler and write results to a file on exit. - Only to be used with eventlet code (aka an StackStorm service minus the CLI). + Only to be used with eventlet/gevent code (aka a StackStorm service minus the CLI). """ - is_patched = eventlet.patcher.is_monkey_patched("os") - if not is_patched: - raise ValueError( - "No eventlet monkey patching detected. Code may not be using eventlet" - ) - - profiler = profile.Profile() - profiler.start() + profiler, start, stop = concurrency.get_green_profiler() + start() file_path = os.path.join( "/tmp", "%s-%s-%s.cprof" % (service_name, platform.machine(), int(time.time())) @@ -72,10 +65,15 @@ def setup_eventlet_profiler(service_name: str) -> None: print("Profiling data will be saved to %s on exit" % (file_path)) def stop_profiler(): - profiler.stop() + stop() profiler.dump_stats(file_path) print("Profiling data written to %s" % (file_path)) print("You can view it using: ") print("\t python3 -m pstats %s" % (file_path)) atexit.register(stop_profiler) + + +def setup_eventlet_profiler(service_name: str) -> None: + """Deprecated alias for setup_green_profiler(), kept for external callers.""" + return setup_green_profiler(service_name) diff --git a/st2reactor/st2reactor/cmd/timersengine.py b/st2reactor/st2reactor/cmd/timersengine.py index 49f91f949c..f85b2ebe98 100644 --- a/st2reactor/st2reactor/cmd/timersengine.py +++ b/st2reactor/st2reactor/cmd/timersengine.py @@ -75,7 +75,7 @@ def _run_worker(): timer = St2Timer(local_timezone=local_tz) timer_thread = concurrency.spawn(_kickoff_timer, timer) LOG.info(TIMER_ENABLED_LOG_LINE) - return timer_thread.wait() + return concurrency.wait(timer_thread) else: LOG.info(TIMER_DISABLED_LOG_LINE) except (KeyboardInterrupt, SystemExit): diff --git a/st2reactor/st2reactor/container/manager.py b/st2reactor/st2reactor/container/manager.py index e9f251aebc..e833000d9c 100644 --- a/st2reactor/st2reactor/container/manager.py +++ b/st2reactor/st2reactor/container/manager.py @@ -83,7 +83,7 @@ def _spin_container_and_wait(self, sensors): LOG.debug("Starting sensor CUD watcher...") self._sensors_watcher.start() - exit_code = self._container_thread.wait() + exit_code = concurrency.wait(self._container_thread) LOG.error("Process container quit with exit_code %d.", exit_code) LOG.error("(PID:%s) SensorContainer stopped.", os.getpid()) except (KeyboardInterrupt, SystemExit): diff --git a/st2reactor/tests/unit/test_timer.py b/st2reactor/tests/unit/test_timer.py index 9ec00be5ec..053e0fba23 100644 --- a/st2reactor/tests/unit/test_timer.py +++ b/st2reactor/tests/unit/test_timer.py @@ -22,6 +22,7 @@ from st2common.models.system.common import ResourceReference from st2common.persistence.trigger import TriggerType from st2common.persistence.trigger import Trigger +from st2common.util import concurrency from st2reactor.timer.base import St2Timer from st2tests.base import CleanDbTestCase @@ -75,7 +76,7 @@ def test_existing_rules_are_loaded_on_start(self): self.assertEqual(len(Trigger.get_all()), 1) timer.start() - timer._trigger_watcher._load_thread.wait() + concurrency.wait(timer._trigger_watcher._load_thread) # Verify handlers are called timer._handle_create_trigger.assert_called_with(trigger_db) diff --git a/st2tests/integration/orquesta/test_performance.py b/st2tests/integration/orquesta/test_performance.py index 899b3090f9..a5a71a40c7 100644 --- a/st2tests/integration/orquesta/test_performance.py +++ b/st2tests/integration/orquesta/test_performance.py @@ -17,13 +17,13 @@ from __future__ import absolute_import -import eventlet import json from integration.orquesta import base from six.moves import range from st2common.constants import action as ac_const +from st2common.util import concurrency class WiringTest(base.TestWorkflowExecution): @@ -35,7 +35,7 @@ def test_concurrent_load(self): wf_input = {"vm_name": "demo1", "meta": {"demo1.itests.org": "10.3.41.99"}} exs = [self._execute_workflow(wf_name, wf_input) for i in range(load_count)] - eventlet.sleep(delay_poll) + concurrency.sleep(delay_poll) for ex in exs: e = self._wait_for_completion(ex) diff --git a/st2tests/integration/orquesta/test_wiring_error_handling.py b/st2tests/integration/orquesta/test_wiring_error_handling.py index 130a68c7c5..81d6a3c9dc 100644 --- a/st2tests/integration/orquesta/test_wiring_error_handling.py +++ b/st2tests/integration/orquesta/test_wiring_error_handling.py @@ -15,10 +15,10 @@ from __future__ import absolute_import -import eventlet from integration.orquesta import base from st2common.constants import action as ac_const +from st2common.util import concurrency class ErrorHandlingTest(base.TestWorkflowExecution): @@ -258,7 +258,7 @@ def test_remediate_then_fail(self): # tasks are written. To avoid that, we use longer sleep delay here. # Better approach would be to try to retry a couple of times until expected num of # tasks is reached (With some hard limit) before failing - eventlet.sleep(2) + concurrency.sleep(2) self._wait_for_task(ex, "task1", ac_const.LIVEACTION_STATUS_FAILED) self._wait_for_task(ex, "log", ac_const.LIVEACTION_STATUS_SUCCEEDED) diff --git a/st2tests/integration/orquesta/test_wiring_inquiry.py b/st2tests/integration/orquesta/test_wiring_inquiry.py index b6bf922758..430f9749b3 100644 --- a/st2tests/integration/orquesta/test_wiring_inquiry.py +++ b/st2tests/integration/orquesta/test_wiring_inquiry.py @@ -15,11 +15,10 @@ from __future__ import absolute_import -import eventlet - from integration.orquesta import base from st2common.constants import action as ac_const +from st2common.util import concurrency class InquiryWiringTest(base.TestWorkflowExecution): @@ -75,7 +74,7 @@ def test_parallel_inquiries(self): ) # Allow some time for the first inquiry to get processed. - eventlet.sleep(2) + concurrency.sleep(2) # Respond to the second inquiry. t2_ac_exs = self._wait_for_task( diff --git a/st2tests/st2tests/__init__.py b/st2tests/st2tests/__init__.py index fc60bd0728..502ebd0575 100644 --- a/st2tests/st2tests/__init__.py +++ b/st2tests/st2tests/__init__.py @@ -15,6 +15,7 @@ from __future__ import absolute_import +from st2tests.base import GreenThreadTestCase from st2tests.base import EventletTestCase from st2tests.base import DbTestCase from st2tests.base import ExecutionDbTestCase @@ -23,6 +24,7 @@ __all__ = [ + "GreenThreadTestCase", "EventletTestCase", "DbTestCase", "ExecutionDbTestCase", diff --git a/st2tests/st2tests/base.py b/st2tests/st2tests/base.py index d28f634239..cf03ba2f0a 100644 --- a/st2tests/st2tests/base.py +++ b/st2tests/st2tests/base.py @@ -36,7 +36,6 @@ import logging import six -import eventlet import psutil from st2common.util import concurrency import mock @@ -179,10 +178,9 @@ def _register_pack_configs(self, validate_configs=False): registrar.register_from_packs(base_dirs=get_packs_base_paths()) -class EventletTestCase(TestCase): +class GreenThreadTestCase(TestCase): """ - Base test class which performs eventlet monkey patching before the tests run - and un-patching after the tests have finished running. + Base test class which performs eventlet/gevent monkey patching before the tests run. """ @classmethod @@ -191,10 +189,14 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - if concurrency.get_concurrency_library() == "eventlet": - eventlet.monkey_patch( - os=False, select=False, socket=False, thread=False, time=False - ) + # NOTE: Intentionally does not call super().tearDownClass() - subclasses that mix this + # in with a DB test case (e.g. ExecutionDbTestCase) rely on this stopping the MRO chain + # here rather than triggering that base class' DB teardown. + pass + + +class EventletTestCase(GreenThreadTestCase): + """Deprecated alias for GreenThreadTestCase, kept for external callers.""" class BaseDbTestCase(BaseTestCase): @@ -816,11 +818,16 @@ def get_resources_path(): return os.path.join(os.path.dirname(__file__), "resources") -def blocking_eventlet_spawn(func, *args, **kwargs): +def blocking_concurrency_spawn(func, *args, **kwargs): func(*args, **kwargs) return mock.Mock() +def blocking_eventlet_spawn(func, *args, **kwargs): + """Deprecated alias for blocking_concurrency_spawn(), kept for external callers.""" + return blocking_concurrency_spawn(func, *args, **kwargs) + + # Utility function for mocking read_and_store_{stdout,stderr} functions def make_mock_stream_readline(mock_stream, mock_data, stop_counter=1, sleep_delay=0): mock_stream.counter = 0 diff --git a/tools/launchdev.sh b/tools/launchdev.sh index 26b4b92268..848aed2d14 100755 --- a/tools/launchdev.sh +++ b/tools/launchdev.sh @@ -198,6 +198,15 @@ function st2start() fi echo -n "Using content packs base dir: "; iecho "$PACKS_BASE_DIR" + # Gunicorn worker class must match the concurrency library st2 itself is using + # (see st2common.util.concurrency), or the WSGI worker will hang: gevent's monkey + # patching happening under gunicorn's eventlet worker (or vice versa) doesn't work. + CONCURRENCY_LIBRARY=$(grep 'concurrency_library' ${ST2_CONF} | awk 'BEGIN {FS=" = "}; {print $2}') + if [ -z "$CONCURRENCY_LIBRARY" ]; then + CONCURRENCY_LIBRARY="gevent" + fi + echo -n "Using concurrency library: "; iecho "$CONCURRENCY_LIBRARY" + # Copy and overwrite the action contents if [ ! -d "$ST2_BASE_DIR" ]; then wecho "$ST2_BASE_DIR doesn't exist. Creating ..." @@ -266,7 +275,7 @@ function st2start() # Run the st2 API server if [ "${use_gunicorn}" = true ]; then echo 'Starting st2-api using gunicorn ...' - tmux new-session -d -s st2-api "${PRE_SCRIPT}; ${VIRTUALENV}/bin/gunicorn st2api.wsgi:application -k eventlet -b $BINDING_ADDRESS:9101 --workers 1 2>&1 | tee -a ${ST2_LOGS}/st2-api.log" + tmux new-session -d -s st2-api "${PRE_SCRIPT}; ${VIRTUALENV}/bin/gunicorn st2api.wsgi:application -k ${CONCURRENCY_LIBRARY} -b $BINDING_ADDRESS:9101 --workers 1 2>&1 | tee -a ${ST2_LOGS}/st2-api.log" else echo 'Starting st2-api ...' tmux new-session -d -s st2-api "${PRE_SCRIPT}; ${VIRTUALENV}/bin/python ./st2api/bin/st2api --config-file $ST2_CONF 2>&1 | tee -a ${ST2_LOGS}/st2-api.log" @@ -275,7 +284,7 @@ function st2start() # Run st2stream API server if [ "${use_gunicorn}" = true ]; then echo 'Starting st2-stream using gunicorn ...' - tmux new-session -d -s st2-stream "${PRE_SCRIPT}; ${VIRTUALENV}/bin/gunicorn st2stream.wsgi:application -k eventlet -b $BINDING_ADDRESS:9102 --workers 1 2>&1 | tee -a ${ST2_LOGS}/st2-stream.log" + tmux new-session -d -s st2-stream "${PRE_SCRIPT}; ${VIRTUALENV}/bin/gunicorn st2stream.wsgi:application -k ${CONCURRENCY_LIBRARY} -b $BINDING_ADDRESS:9102 --workers 1 2>&1 | tee -a ${ST2_LOGS}/st2-stream.log" else echo 'Starting st2-stream ...' tmux new-session -d -s st2-stream "${PRE_SCRIPT}; ${VIRTUALENV}/bin/python ./st2stream/bin/st2stream --config-file $ST2_CONF 2>&1 | tee -a ${ST2_LOGS}/st2-stream.log" @@ -340,7 +349,7 @@ function st2start() # Run the auth API server if [ "${use_gunicorn}" = true ]; then echo 'Starting st2-auth using gunicorn ...' - tmux new-session -d -s st2-auth "${PRE_SCRIPT}; ${VIRTUALENV}/bin/gunicorn st2auth.wsgi:application -k eventlet -b $BINDING_ADDRESS:9100 --workers 1 2>&1 | tee -a ${ST2_LOGS}/st2-auth.log" + tmux new-session -d -s st2-auth "${PRE_SCRIPT}; ${VIRTUALENV}/bin/gunicorn st2auth.wsgi:application -k ${CONCURRENCY_LIBRARY} -b $BINDING_ADDRESS:9100 --workers 1 2>&1 | tee -a ${ST2_LOGS}/st2-auth.log" else echo 'Starting st2-auth ...' tmux new-session -d -s st2-auth "${PRE_SCRIPT}; ${VIRTUALENV}/bin/python ./st2auth/bin/st2auth --config-file $ST2_CONF 2>&1 | tee -a ${ST2_LOGS}/st2-auth.log" diff --git a/tools/queue_producer.py b/tools/queue_producer.py index 01476a26b8..4b3ea4f42c 100755 --- a/tools/queue_producer.py +++ b/tools/queue_producer.py @@ -22,18 +22,18 @@ import argparse -import eventlet from kombu import Exchange from st2common import config from st2common.transport.publishers import PoolPublisher +from st2common.util import concurrency def main(exchange, routing_key, payload): exchange = Exchange(exchange, type="topic") publisher = PoolPublisher() publisher.publish(payload=payload, exchange=exchange, routing_key=routing_key) - eventlet.sleep(0.5) + concurrency.sleep(0.5) if __name__ == "__main__": diff --git a/tools/st2-inject-trigger-instances.py b/tools/st2-inject-trigger-instances.py index 79b0f18e25..9a2aa1031c 100755 --- a/tools/st2-inject-trigger-instances.py +++ b/tools/st2-inject-trigger-instances.py @@ -30,12 +30,12 @@ import os import random -import eventlet from oslo_config import cfg import yaml from st2common import config from st2common.util.monkey_patch import monkey_patch +from st2common.util import concurrency from st2common.util import date as date_utils from st2common.transport.reactor import TriggerDispatcher @@ -67,7 +67,7 @@ def _inject_instances( # NOTE: We decrease sleep delay for 56% to take into account overhead / delay because # of the call to dispatchet.dispatch method. delta = random.expovariate(rate_per_trigger) - eventlet.sleep(delta * 0.56) + concurrency.sleep(delta * 0.56) elapsed = (date_utils.get_datetime_utc_now() - start).seconds count += 1 @@ -155,7 +155,7 @@ def main(): rate = 0 rate_per_trigger = 0 - dispatcher_pool = eventlet.GreenPool(len(triggers)) + dispatcher_pool = concurrency.get_green_pool_class()(len(triggers)) for trigger in triggers: payload = trigger_payload_schema.get(trigger, {}) @@ -167,8 +167,8 @@ def main(): payload=payload, max_throughput=max_throughput, ) - eventlet.sleep(random.uniform(0, 1)) - dispatcher_pool.waitall() + concurrency.sleep(random.uniform(0, 1)) + concurrency.green_pool_wait_all(dispatcher_pool) if __name__ == "__main__": From 30728424287eea5de6ebc2a30b6dd411cf1854af Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Wed, 12 Aug 2026 15:27:01 -0600 Subject: [PATCH 12/22] test: explicitly set concurrency_library in test conf files Both conf/st2.tests.conf (used by subprocess-spawning integration tests) and st2tests/conf/st2.conf now explicitly set concurrency_library = gevent instead of silently relying on the code-level fallback default. --- conf/st2.tests.conf | 1 + st2tests/conf/st2.conf | 1 + 2 files changed, 2 insertions(+) diff --git a/conf/st2.tests.conf b/conf/st2.tests.conf index d4301d0433..82b5b574e0 100644 --- a/conf/st2.tests.conf +++ b/conf/st2.tests.conf @@ -44,6 +44,7 @@ api_url = http://127.0.0.1:9101/ [system] # This way integration tests can write to this directory base_path = /tmp +concurrency_library = gevent [garbagecollector] logging = st2reactor/conf/logging.garbagecollector.conf diff --git a/st2tests/conf/st2.conf b/st2tests/conf/st2.conf index 0801ccc4a7..b0b7e8e961 100644 --- a/st2tests/conf/st2.conf +++ b/st2tests/conf/st2.conf @@ -28,6 +28,7 @@ logging = st2auth/conf/logging.conf [system] base_path = /opt/stackstorm +concurrency_library = gevent [syslog] host = 127.0.0.1 From 62637e01bd6b30270f18564dfe83ff60b363b336 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Wed, 12 Aug 2026 16:12:02 -0600 Subject: [PATCH 13/22] style: apply black formatting to test_gunicorn_configs.py --- st2api/tests/integration/test_gunicorn_configs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/st2api/tests/integration/test_gunicorn_configs.py b/st2api/tests/integration/test_gunicorn_configs.py index eff77b695e..77bf1e55ca 100644 --- a/st2api/tests/integration/test_gunicorn_configs.py +++ b/st2api/tests/integration/test_gunicorn_configs.py @@ -40,9 +40,9 @@ class GunicornWSGIEntryPointTestCase(IntegrationTestCase): @pytest.mark.skipif(profiling.is_enabled(), reason="Profiling is enabled") def test_st2api_wsgi_entry_point(self): port = random.randint(10000, 30000) - cmd = ( - 'gunicorn st2api.wsgi:application -k %s -b "127.0.0.1:%s" --workers 1' - % (GUNICORN_WORKER_CLASS, port) + cmd = 'gunicorn st2api.wsgi:application -k %s -b "127.0.0.1:%s" --workers 1' % ( + GUNICORN_WORKER_CLASS, + port, ) env = os.environ.copy() env["ST2_CONFIG_PATH"] = ST2_CONFIG_PATH From 57a10300599b971a329204ba2eadd5b4f221906b Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Thu, 13 Aug 2026 12:39:08 -0600 Subject: [PATCH 14/22] fix(concurrency): guarantee monkey patching before tooz import; simplify library loading conftest.py now monkey patches before any test module is imported, whether pytest runs a whole directory or a single file in isolation (as Pants does in CI). Without this, tooz's Heart binds threading.Thread/Event as default args at import time; if tooz gets imported before patching, it permanently uses the unpatched primitives, causing gevent.exceptions.LoopExit or hangs in test_synchronization.py, test_worker.py, and test_service_registry.py. concurrency.py now only imports the concurrency library that's actually active, replaces the CONCURRENCY_LIBRARY/eventlet/gevent globals with a single state object, and consolidates every eventlet/gevent submodule import into one place instead of scattering local imports across each helper function. --- conftest.py | 10 + st2common/st2common/util/concurrency.py | 290 ++++++++++++------------ 2 files changed, 159 insertions(+), 141 deletions(-) diff --git a/conftest.py b/conftest.py index 8bfc45b048..c112ab1f81 100644 --- a/conftest.py +++ b/conftest.py @@ -14,6 +14,16 @@ import hashlib from typing import Iterable, List, Sequence +# NOTE: This must happen before any test module imports anything that pulls in tooz/eventlet +# (e.g. st2common.services.coordination). Tooz's Heart binds threading.Thread/Event as default +# args at import time, so if it's imported before monkey patching, it permanently uses the +# unpatched primitives -- causing gevent.exceptions.LoopExit or hangs later. Pytest always loads +# this file before any test module, in both whole-directory and single-file (Pants) runs, so this +# is the one place that reliably wins the race regardless of which test file happens to run first. +from st2common.util.monkey_patch import monkey_patch + +monkey_patch() + from _pytest import nodes from _pytest.config import create_terminal_writer diff --git a/st2common/st2common/util/concurrency.py b/st2common/st2common/util/concurrency.py index 0d21aa5230..1f38cf1df8 100644 --- a/st2common/st2common/util/concurrency.py +++ b/st2common/st2common/util/concurrency.py @@ -22,22 +22,28 @@ import configparser import cProfile +import types from st2common.constants.system import DEFAULT_CONFIG_FILE_PATH -try: - import eventlet # pylint: disable=import-error -except ImportError: - eventlet = None +__all__ = [ + "set_concurrency_library", + "get_concurrency_library", + "get_subprocess_module", + "subprocess_popen", + "spawn", + "wait", + "cancel", + "kill", + "sleep", + "get_greenlet_exit_exception_class", + "get_green_pool_class", + "is_green_pool_free", + "green_pool_wait_all", + "get_green_profiler", +] -try: - import gevent # pylint: disable=import-error # pants: no-infer-dep - import gevent.lock - import gevent.monkey - import gevent.pool - import gevent.queue -except ImportError: - gevent = None +_state = types.SimpleNamespace(library=None, eventlet=None, gevent=None) def _get_concurrency_library_from_conf(config_path=DEFAULT_CONFIG_FILE_PATH): @@ -53,60 +59,70 @@ def _get_concurrency_library_from_conf(config_path=DEFAULT_CONFIG_FILE_PATH): return "gevent" -CONCURRENCY_LIBRARY = _get_concurrency_library_from_conf() +def _import_eventlet(): + try: + import eventlet # pylint: disable=import-error + import eventlet.debug + import eventlet.green.profile + import eventlet.green.subprocess + import eventlet.wsgi -__all__ = [ - "set_concurrency_library", - "get_concurrency_library", - "get_subprocess_module", - "subprocess_popen", - "spawn", - "wait", - "cancel", - "kill", - "sleep", - "get_greenlet_exit_exception_class", - "get_green_pool_class", - "is_green_pool_free", - "green_pool_wait_all", - "get_green_profiler", -] + return eventlet + except ImportError: + return None -def set_concurrency_library(library): - global CONCURRENCY_LIBRARY +def _import_gevent(): + try: + import gevent # pylint: disable=import-error # pants: no-infer-dep + import gevent.lock + import gevent.monkey + import gevent.pool + import gevent.pywsgi + import gevent.queue + import gevent.subprocess - if library not in ["eventlet", "gevent"]: + return gevent + except ImportError: + return None + + +def set_concurrency_library(library): + if library not in ("eventlet", "gevent"): raise ValueError("Unsupported concurrency library: %s" % (library)) - CONCURRENCY_LIBRARY = library + # Only import the library that's actually active. Importing eventlet unconditionally + # eagerly loads eventlet.green.ssl, subclassing ssl.SSLContext before + # gevent.monkey.patch_all() runs, which triggers a spurious MonkeyPatchWarning. + if library == "eventlet" and _state.eventlet is None: + _state.eventlet = _import_eventlet() + elif library == "gevent" and _state.gevent is None: + _state.gevent = _import_gevent() + + _state.library = library def get_concurrency_library(): - global CONCURRENCY_LIBRARY - return CONCURRENCY_LIBRARY + return _state.library -def get_subprocess_module(): - if CONCURRENCY_LIBRARY == "eventlet": - from eventlet.green import subprocess # pylint: disable=import-error +set_concurrency_library(_get_concurrency_library_from_conf()) - return subprocess - elif CONCURRENCY_LIBRARY == "gevent": - from gevent import subprocess # pylint: disable=import-error - return subprocess +def get_subprocess_module(): + if _state.library == "eventlet": + return _state.eventlet.green.subprocess + elif _state.library == "gevent": + return _state.gevent.subprocess else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def wsgi_server( socket, app, custom_pool=None, log=None, log_output=True, *args, **kwargs ): - if CONCURRENCY_LIBRARY == "eventlet": - from eventlet import wsgi - - wsgi.server( + if _state.library == "eventlet": + _state.eventlet.wsgi.server( socket, app, custom_pool=custom_pool, @@ -115,83 +131,77 @@ def wsgi_server( *args, **kwargs, ) - elif CONCURRENCY_LIBRARY == "gevent": - from gevent import pywsgi - - server = pywsgi.WSGIServer(socket, app, spawn=custom_pool, log=log) + elif _state.library == "gevent": + server = _state.gevent.pywsgi.WSGIServer(socket, app, spawn=custom_pool, log=log) server.serve_forever() else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def subprocess_popen(*args, **kwargs): - if CONCURRENCY_LIBRARY == "eventlet": - from eventlet.green import subprocess # pylint: disable=import-error - - return subprocess.Popen(*args, **kwargs) - elif CONCURRENCY_LIBRARY == "gevent": - from gevent import subprocess # pylint: disable=import-error - - return subprocess.Popen(*args, **kwargs) + if _state.library == "eventlet": + return _state.eventlet.green.subprocess.Popen(*args, **kwargs) + elif _state.library == "gevent": + return _state.gevent.subprocess.Popen(*args, **kwargs) else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def spawn_after(seconds, func, *args, **kwargs): - if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.spawn_after(seconds, func, *args, **kwargs) - elif CONCURRENCY_LIBRARY == "gevent": - return gevent.spawn_later(seconds, func, *args, **kwargs) + if _state.library == "eventlet": + return _state.eventlet.spawn_after(seconds, func, *args, **kwargs) + elif _state.library == "gevent": + return _state.gevent.spawn_later(seconds, func, *args, **kwargs) else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def Semaphore(*args, **kwargs): - if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.Semaphore(*args, **kwargs) - elif CONCURRENCY_LIBRARY == "gevent": - return gevent.lock.Semaphore(*args, **kwargs) + if _state.library == "eventlet": + return _state.eventlet.Semaphore(*args, **kwargs) + elif _state.library == "gevent": + return _state.gevent.lock.Semaphore(*args, **kwargs) else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def spawn(func, *args, **kwargs): - if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.spawn(func, *args, **kwargs) - elif CONCURRENCY_LIBRARY == "gevent": - return gevent.spawn(func, *args, **kwargs) + if _state.library == "eventlet": + return _state.eventlet.spawn(func, *args, **kwargs) + elif _state.library == "gevent": + return _state.gevent.spawn(func, *args, **kwargs) else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def wait(green_thread, *args, **kwargs): - if CONCURRENCY_LIBRARY == "eventlet": + if _state.library == "eventlet": return green_thread.wait(*args, **kwargs) - elif CONCURRENCY_LIBRARY == "gevent": + elif _state.library == "gevent": # NOTE: Greenlet.join() blocks but always returns None; .get() blocks # and returns the greenlet's result (or re-raises its exception), # matching eventlet's GreenThread.wait() semantics. return green_thread.get(*args, **kwargs) else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def cancel(green_thread, *args, **kwargs): - if CONCURRENCY_LIBRARY == "eventlet": + if _state.library == "eventlet": return green_thread.cancel(*args, **kwargs) - elif CONCURRENCY_LIBRARY == "gevent": + elif _state.library == "gevent": return green_thread.kill(*args, **kwargs) else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def kill(green_thread, *args, **kwargs): - if CONCURRENCY_LIBRARY == "eventlet": + if _state.library == "eventlet": return green_thread.kill(*args, **kwargs) - elif CONCURRENCY_LIBRARY == "gevent": + elif _state.library == "gevent": return green_thread.kill(*args, **kwargs) else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def listen(host, port): @@ -199,45 +209,45 @@ def listen(host, port): def Queue(*args, **kwargs): - if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.Queue(*args, **kwargs) - elif CONCURRENCY_LIBRARY == "gevent": - return gevent.queue.Queue(*args, **kwargs) + if _state.library == "eventlet": + return _state.eventlet.Queue(*args, **kwargs) + elif _state.library == "gevent": + return _state.gevent.queue.Queue(*args, **kwargs) else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def get_queue_empty_exception(): - if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.queue.Empty - elif CONCURRENCY_LIBRARY == "gevent": - return gevent.queue.Empty + if _state.library == "eventlet": + return _state.eventlet.queue.Empty + elif _state.library == "gevent": + return _state.gevent.queue.Empty else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def sleep(*args, **kwargs): - if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.sleep(*args, **kwargs) - elif CONCURRENCY_LIBRARY == "gevent": - return gevent.sleep(*args, **kwargs) + if _state.library == "eventlet": + return _state.eventlet.sleep(*args, **kwargs) + elif _state.library == "gevent": + return _state.gevent.sleep(*args, **kwargs) else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def get_greenlet_exit_exception_class(): - if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.support.greenlets.GreenletExit - elif CONCURRENCY_LIBRARY == "gevent": - return gevent.GreenletExit + if _state.library == "eventlet": + return _state.eventlet.support.greenlets.GreenletExit + elif _state.library == "gevent": + return _state.gevent.GreenletExit else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def get_default_green_pool_size(): - if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.wsgi.DEFAULT_MAX_SIMULTANEOUS_REQUESTS - elif CONCURRENCY_LIBRARY == "gevent": + if _state.library == "eventlet": + return _state.eventlet.wsgi.DEFAULT_MAX_SIMULTANEOUS_REQUESTS + elif _state.library == "gevent": # matches what DEFAULT_MAX_SIMULTANEOUS_REQUESTS is for eventlet return 1024 else: @@ -245,24 +255,24 @@ def get_default_green_pool_size(): def get_green_pool_class(): - if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.GreenPool - elif CONCURRENCY_LIBRARY == "gevent": - return gevent.pool.Pool + if _state.library == "eventlet": + return _state.eventlet.GreenPool + elif _state.library == "gevent": + return _state.gevent.pool.Pool else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def green_pool_free_count(pool): """ Return the number of free slots in the pool. """ - if CONCURRENCY_LIBRARY == "eventlet": + if _state.library == "eventlet": return pool.free() - elif CONCURRENCY_LIBRARY == "gevent": + elif _state.library == "gevent": return pool.free_count() else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def is_green_pool_free(pool): @@ -276,9 +286,9 @@ def green_pool_running_count(pool): """ Return the number of greenlets currently running in the pool. """ - if CONCURRENCY_LIBRARY == "eventlet": + if _state.library == "eventlet": return pool.running() - elif CONCURRENCY_LIBRARY == "gevent": + elif _state.library == "gevent": return len(pool.greenlets) else: raise ValueError("Unsupported concurrency library") @@ -288,9 +298,9 @@ def get_pool_greenlets(pool): """ Return the set of currently running greenlets in the pool. """ - if CONCURRENCY_LIBRARY == "eventlet": + if _state.library == "eventlet": return pool.coroutines_running - elif CONCURRENCY_LIBRARY == "gevent": + elif _state.library == "gevent": return pool.greenlets else: raise ValueError("Unsupported concurrency library") @@ -300,9 +310,9 @@ def green_pool_wait_all(pool): """ Wait for all the green threads in the pool to finish. """ - if CONCURRENCY_LIBRARY == "eventlet": + if _state.library == "eventlet": return pool.waitall() - elif CONCURRENCY_LIBRARY == "gevent": + elif _state.library == "gevent": return pool.join() else: raise ValueError("Unsupported concurrency library") @@ -313,9 +323,9 @@ def listen_server(host, port, backlog=50, **kwargs): Start listening on the host:port. :backlog: the number of unaccepted connections that the system will allow before refusing new connections. """ - if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.listen((host, port), backlog=backlog, **kwargs) - elif CONCURRENCY_LIBRARY == "gevent": + if _state.library == "eventlet": + return _state.eventlet.listen((host, port), backlog=backlog, **kwargs) + elif _state.library == "gevent": import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -328,9 +338,9 @@ def listen_server(host, port, backlog=50, **kwargs): def wrap_ssl(socket, *args, **kwargs): - if CONCURRENCY_LIBRARY == "eventlet": - return eventlet.wrap_ssl(socket, *args, **kwargs) - elif CONCURRENCY_LIBRARY == "gevent": + if _state.library == "eventlet": + return _state.eventlet.wrap_ssl(socket, *args, **kwargs) + elif _state.library == "gevent": # Monkey patching in the caller module is required prior to # calling wrap_ssl() or this may block. import ssl @@ -355,18 +365,16 @@ def get_green_profiler(): Only to be used with eventlet/gevent code (aka a StackStorm service minus the CLI). """ - if CONCURRENCY_LIBRARY == "eventlet": - if not eventlet.patcher.is_monkey_patched("os"): + if _state.library == "eventlet": + if not _state.eventlet.patcher.is_monkey_patched("os"): raise ValueError( "No eventlet monkey patching detected. Code may not be using eventlet" ) - from eventlet.green import profile - - profiler = profile.Profile() + profiler = _state.eventlet.green.profile.Profile() return profiler, profiler.start, profiler.stop - elif CONCURRENCY_LIBRARY == "gevent": - if not gevent.monkey.is_module_patched("os"): + elif _state.library == "gevent": + if not _state.gevent.monkey.is_module_patched("os"): raise ValueError( "No gevent monkey patching detected. Code may not be using gevent" ) @@ -376,20 +384,20 @@ def get_green_profiler(): profiler = cProfile.Profile() return profiler, profiler.enable, profiler.disable else: - raise ValueError(f"Unsupported concurrency library {CONCURRENCY_LIBRARY}") + raise ValueError(f"Unsupported concurrency library {_state.library}") def blocking_detection(enable=False, timeout=1.0): - if CONCURRENCY_LIBRARY == "eventlet": + if _state.library == "eventlet": print( f"Eventlet long running / blocking operation detection logic enabled. Block timeout ({timeout})." ) - eventlet.debug.hub_blocking_detection(state=enable, resolution=timeout) - elif CONCURRENCY_LIBRARY == "gevent": + _state.eventlet.debug.hub_blocking_detection(state=enable, resolution=timeout) + elif _state.library == "gevent": print( f"gEvent long running / blocking operation detection logic enabled. Block timeout ({timeout})." ) - gevent.config.monitor_thread = enable - gevent.config.max_blocking_time = timeout + _state.gevent.config.monitor_thread = enable + _state.gevent.config.max_blocking_time = timeout else: raise ValueError("Unsupported concurrency library") From 8f2de5fcb423088026e4e01f820d41b34bfc747c Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Thu, 13 Aug 2026 13:14:00 -0600 Subject: [PATCH 15/22] style: black formatting fix for concurrency.py --- st2common/st2common/util/concurrency.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/st2common/st2common/util/concurrency.py b/st2common/st2common/util/concurrency.py index 1f38cf1df8..18a6031b4c 100644 --- a/st2common/st2common/util/concurrency.py +++ b/st2common/st2common/util/concurrency.py @@ -132,7 +132,9 @@ def wsgi_server( **kwargs, ) elif _state.library == "gevent": - server = _state.gevent.pywsgi.WSGIServer(socket, app, spawn=custom_pool, log=log) + server = _state.gevent.pywsgi.WSGIServer( + socket, app, spawn=custom_pool, log=log + ) server.serve_forever() else: raise ValueError(f"Unsupported concurrency library {_state.library}") From 0895139a719e8a74b505b43e22c57fd8a787a931 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Wed, 19 Aug 2026 14:28:28 -0600 Subject: [PATCH 16/22] fix(concurrency): wrap remaining direct kill/link/spawn/resize bypasses Sweep of every file importing concurrency.py for objects created via its factory functions (spawn/Semaphore/Queue/green pools) that still called library-specific methods directly instead of going through the wrapper. Adds link(), pool_spawn(), and resize_green_pool() wrappers and updates their call sites in actionrunner.py, workflow_engine.py, scheduler/handler.py, greenpooldispatch.py, wsgi.py, parallel_ssh.py, consumers.py, st2-inject-trigger-instances.py, and the corresponding test files. Also defers actionrunner.py/workflow_engine.py's SIGTERM handlers to concurrency.spawn(...) instead of calling .kill() synchronously from the raw signal callback, since gevent raises BlockingSwitchOutError for blocking greenlet operations invoked directly from a signal handler (eventlet has no such restriction). Co-Authored-By: Claude Sonnet 5 --- st2actions/st2actions/cmd/actionrunner.py | 6 ++- st2actions/st2actions/cmd/workflow_engine.py | 6 ++- st2actions/st2actions/scheduler/handler.py | 8 ++-- .../test_actions_queue_consumer.py | 2 +- st2actions/tests/unit/test_worker.py | 6 +-- st2common/st2common/runners/parallel_ssh.py | 7 ++- st2common/st2common/transport/consumers.py | 2 +- st2common/st2common/util/concurrency.py | 43 +++++++++++++++++++ st2common/st2common/util/greenpooldispatch.py | 4 +- st2common/st2common/util/wsgi.py | 2 +- st2common/tests/unit/test_transport.py | 2 +- .../tests/unit/test_process_container.py | 2 +- tools/st2-inject-trigger-instances.py | 3 +- 13 files changed, 75 insertions(+), 18 deletions(-) diff --git a/st2actions/st2actions/cmd/actionrunner.py b/st2actions/st2actions/cmd/actionrunner.py index 05f48f6464..fad4b52cb1 100644 --- a/st2actions/st2actions/cmd/actionrunner.py +++ b/st2actions/st2actions/cmd/actionrunner.py @@ -31,6 +31,7 @@ from st2common.service_setup import setup as common_setup from st2common.service_setup import teardown as common_teardown from st2common.service_setup import deregister_service +from st2common.util import concurrency __all__ = ["main"] @@ -41,7 +42,10 @@ def _setup_sigterm_handler(action_worker): def sigterm_handler(signum=None, frame=None): # This will cause SystemExit to be throw and allow for component cleanup. - action_worker.kill() + # action_worker.kill() blocks waiting for the greenlet to die, which gevent + # forbids doing directly inside a signal callback (BlockingSwitchOutError), + # so defer it to a freshly spawned greenlet instead. + concurrency.spawn(action_worker.kill) # Register a SIGTERM signal handler which calls sys.exit which causes SystemExit to # be thrown. We catch SystemExit and handle cleanup there. diff --git a/st2actions/st2actions/cmd/workflow_engine.py b/st2actions/st2actions/cmd/workflow_engine.py index ac6626afde..aa151ead0a 100644 --- a/st2actions/st2actions/cmd/workflow_engine.py +++ b/st2actions/st2actions/cmd/workflow_engine.py @@ -33,6 +33,7 @@ from st2common.service_setup import setup as common_setup from st2common.service_setup import teardown as common_teardown from st2common.service_setup import deregister_service +from st2common.util import concurrency __all__ = ["main"] @@ -42,7 +43,10 @@ def setup_sigterm_handler(engine): def sigterm_handler(signum=None, frame=None): # This will cause SystemExit to be throw and allow for component cleanup. - engine.kill() + # engine.kill() blocks waiting for the greenlet to die, which gevent forbids + # doing directly inside a signal callback (BlockingSwitchOutError), so defer + # it to a freshly spawned greenlet instead. + concurrency.spawn(engine.kill) # Register a SIGTERM signal handler which calls sys.exit which causes SystemExit to # be thrown. We catch SystemExit and handle cleanup there. diff --git a/st2actions/st2actions/scheduler/handler.py b/st2actions/st2actions/scheduler/handler.py index 4ea092fcb6..5043b7e2ce 100644 --- a/st2actions/st2actions/scheduler/handler.py +++ b/st2actions/st2actions/scheduler/handler.py @@ -83,7 +83,9 @@ def process(self): execution_queue_item_db = self._get_next_execution() if execution_queue_item_db: - self._pool.spawn(self._handle_execution, execution_queue_item_db) + concurrency.pool_spawn( + self._pool, self._handle_execution, execution_queue_item_db + ) def cleanup(self): LOG.debug("Starting scheduler garbage collection...") @@ -504,8 +506,8 @@ def start(self): # Link the threads to the shutdown function. If either of the threads exited with error, # then initiate shutdown which will allow the waits below to throw exception to the # main process. - self._main_thread.link(self.shutdown) - self._cleanup_thread.link(self.shutdown) + concurrency.link(self._main_thread, self.shutdown) + concurrency.link(self._cleanup_thread, self.shutdown) def shutdown(self, *args, **kwargs): if not self._shutdown: diff --git a/st2actions/tests/integration/test_actions_queue_consumer.py b/st2actions/tests/integration/test_actions_queue_consumer.py index f03b935d29..1832020262 100644 --- a/st2actions/tests/integration/test_actions_queue_consumer.py +++ b/st2actions/tests/integration/test_actions_queue_consumer.py @@ -63,7 +63,7 @@ def test_stop_consumption_on_shutdown(self): publisher.publish(payload=body, exchange=exchange) # Second published message won't be consumed. self.assertEqual(self.message_count, 1) - watcher_thread.kill() + concurrency.kill(watcher_thread) def process(self, liveaction): self.message_count = self.message_count + 1 diff --git a/st2actions/tests/unit/test_worker.py b/st2actions/tests/unit/test_worker.py index 3041c90a99..49cc8bbd86 100644 --- a/st2actions/tests/unit/test_worker.py +++ b/st2actions/tests/unit/test_worker.py @@ -267,7 +267,7 @@ def test_worker_graceful_shutdown_with_multiple_runners(self): # _run_action but will not result in KeyError because the discard method is used to # to remove the liveaction from _running_liveactions. concurrency.wait(runner_thread) - shutdown_thread.kill() + concurrency.kill(shutdown_thread) def test_worker_graceful_shutdown_with_single_runner(self): self.reset_config( @@ -332,7 +332,7 @@ def test_worker_graceful_shutdown_with_single_runner(self): # _run_action but will not result in KeyError because the discard method is used to # to remove the liveaction from _running_liveactions. concurrency.wait(runner_thread) - shutdown_thread.kill() + concurrency.kill(shutdown_thread) @mock.patch.object( RedisDriver, @@ -398,4 +398,4 @@ def test_worker_graceful_shutdown_exit_timeout(self): # _run_action but will not result in KeyError because the discard method is used to # to remove the liveaction from _running_liveactions. concurrency.wait(runner_thread) - shutdown_thread.kill() + concurrency.kill(shutdown_thread) diff --git a/st2common/st2common/runners/parallel_ssh.py b/st2common/st2common/runners/parallel_ssh.py index bcf4f8f086..7c150d70d3 100644 --- a/st2common/st2common/runners/parallel_ssh.py +++ b/st2common/st2common/runners/parallel_ssh.py @@ -106,7 +106,8 @@ def connect(self, raise_on_any_error=False): for host in self._hosts: while not concurrency_lib.is_green_pool_free(self._pool): concurrency_lib.sleep(self._scan_interval) - self._pool.spawn( + concurrency_lib.pool_spawn( + self._pool, self._connect, host=host, results=results, @@ -244,7 +245,9 @@ def _execute_in_pool(self, execute_method, **kwargs): for host in self._hosts_client.keys(): while not concurrency_lib.is_green_pool_free(self._pool): concurrency_lib.sleep(self._scan_interval) - self._pool.spawn(execute_method, host=host, results=results, **kwargs) + concurrency_lib.pool_spawn( + self._pool, execute_method, host=host, results=results, **kwargs + ) concurrency_lib.green_pool_wait_all(self._pool) return results diff --git a/st2common/st2common/transport/consumers.py b/st2common/st2common/transport/consumers.py index 853826c7c6..61d7e3fad8 100644 --- a/st2common/st2common/transport/consumers.py +++ b/st2common/st2common/transport/consumers.py @@ -207,7 +207,7 @@ def shutdown(self): self._queue_consumer.shutdown() def kill(self): - self._consumer_thread.kill(SystemExit()) + concurrency.kill(self._consumer_thread, SystemExit()) @abc.abstractmethod def process(self, message): diff --git a/st2common/st2common/util/concurrency.py b/st2common/st2common/util/concurrency.py index 18a6031b4c..50f9c3442f 100644 --- a/st2common/st2common/util/concurrency.py +++ b/st2common/st2common/util/concurrency.py @@ -35,9 +35,12 @@ "wait", "cancel", "kill", + "link", "sleep", "get_greenlet_exit_exception_class", "get_green_pool_class", + "pool_spawn", + "resize_green_pool", "is_green_pool_free", "green_pool_wait_all", "get_green_profiler", @@ -197,6 +200,17 @@ def cancel(green_thread, *args, **kwargs): raise ValueError(f"Unsupported concurrency library {_state.library}") +def link(green_thread, callback): + """ + Register a callback to run once the green thread finishes (whether it succeeded, failed, + or was killed). + """ + if _state.library in ("eventlet", "gevent"): + return green_thread.link(callback) + else: + raise ValueError(f"Unsupported concurrency library {_state.library}") + + def kill(green_thread, *args, **kwargs): if _state.library == "eventlet": return green_thread.kill(*args, **kwargs) @@ -265,6 +279,35 @@ def get_green_pool_class(): raise ValueError(f"Unsupported concurrency library {_state.library}") +def pool_spawn(pool, func, *args, **kwargs): + """ + Spawn a green thread inside the given green pool (bounded concurrency), unlike spawn() + which spawns an unbounded green thread. + """ + if _state.library in ("eventlet", "gevent"): + return pool.spawn(func, *args, **kwargs) + else: + raise ValueError(f"Unsupported concurrency library {_state.library}") + + +def resize_green_pool(pool, size): + """ + Resize a green pool's concurrency limit. + + gevent's Pool has no equivalent of eventlet's GreenPool.resize(): its internal semaphore + is fixed at construction time and never re-reads the (otherwise inert) .size attribute + afterwards, so this is a no-op under gevent. Callers that use this to stop a pool from + accepting new work should also close the pool's listening socket, which is what actually + prevents new work from arriving under either library. + """ + if _state.library == "eventlet": + pool.resize(size) + elif _state.library == "gevent": + pass + else: + raise ValueError(f"Unsupported concurrency library {_state.library}") + + def green_pool_free_count(pool): """ Return the number of free slots in the pool. diff --git a/st2common/st2common/util/greenpooldispatch.py b/st2common/st2common/util/greenpooldispatch.py index 4ad5eab872..81eb52656b 100644 --- a/st2common/st2common/util/greenpooldispatch.py +++ b/st2common/st2common/util/greenpooldispatch.py @@ -67,7 +67,7 @@ def dispatch(self, handler, *args): self._flush_now() def shutdown(self): - self._dispatch_monitor_thread.kill() + concurrency.kill(self._dispatch_monitor_thread) def _flush(self): while True: @@ -95,7 +95,7 @@ def _flush_now(self): self._dispatcher_pool ): (handler, args) = self._work_buffer.get_nowait() - self._dispatcher_pool.spawn(handler, *args) + concurrency.pool_spawn(self._dispatcher_pool, handler, *args) def __repr__(self): free_count = concurrency.green_pool_free_count(self._dispatcher_pool) diff --git a/st2common/st2common/util/wsgi.py b/st2common/st2common/util/wsgi.py index b7b77f3ba5..23e18357a0 100644 --- a/st2common/st2common/util/wsgi.py +++ b/st2common/st2common/util/wsgi.py @@ -40,7 +40,7 @@ def shutdown_server_kill_pending_requests(sock, worker_pool, wait_time=2): forcefully killing them. :type wait_time: ``int`` """ - worker_pool.resize(0) + concurrency.resize_green_pool(worker_pool, 0) sock.close() active_requests = concurrency.green_pool_running_count(worker_pool) diff --git a/st2common/tests/unit/test_transport.py b/st2common/tests/unit/test_transport.py index abade10e0d..1b05dbb0ff 100644 --- a/st2common/tests/unit/test_transport.py +++ b/st2common/tests/unit/test_transport.py @@ -142,7 +142,7 @@ def test_publish_compression(self): ) self.assertEqual(watcher.received_messages[2][0].id, live_action_db.id) - watcher_thread.kill() + concurrency.kill(watcher_thread) def test_get_ssl_kwargs(self): # 1. No SSL kwargs provided diff --git a/st2reactor/tests/unit/test_process_container.py b/st2reactor/tests/unit/test_process_container.py index 747618a4f0..d041198a9c 100644 --- a/st2reactor/tests/unit/test_process_container.py +++ b/st2reactor/tests/unit/test_process_container.py @@ -48,7 +48,7 @@ def test_no_sensors_dont_quit(self): self.assertEqual(process_container.running(), 0) self.assertEqual(process_container.stopped(), False) process_container.shutdown() - process_container_thread.kill() + concurrency.kill(process_container_thread) @patch.object( ProcessSensorContainer, diff --git a/tools/st2-inject-trigger-instances.py b/tools/st2-inject-trigger-instances.py index 9a2aa1031c..b627f193be 100755 --- a/tools/st2-inject-trigger-instances.py +++ b/tools/st2-inject-trigger-instances.py @@ -159,7 +159,8 @@ def main(): for trigger in triggers: payload = trigger_payload_schema.get(trigger, {}) - dispatcher_pool.spawn( + concurrency.pool_spawn( + dispatcher_pool, _inject_instances, trigger, rate_per_trigger, From 1ecee2500360e17082fbed9815057beafb7645a1 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Thu, 20 Aug 2026 09:28:22 -0600 Subject: [PATCH 17/22] Add CHANGELOG entry for gevent support --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3c90a1aaf0..4a77ffbcbf 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,8 @@ Changelog in development -------------- +* Added support for gevent to replace eventlet. Going forward, the default will be gevent, and can be overriden in the conf file `system.concurrency_library = gevent|eventlet` + * implemented zstandard compression for parameters and results. #5995 contributed by @guzzijones12 From 4f62da37d35097edd9154bc6f68c23e7b922d6f8 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Thu, 20 Aug 2026 09:29:52 -0600 Subject: [PATCH 18/22] Update CHANGELOG entry with author and PR reference --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4a77ffbcbf..5a34db6590 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,7 +3,7 @@ Changelog in development -------------- -* Added support for gevent to replace eventlet. Going forward, the default will be gevent, and can be overriden in the conf file `system.concurrency_library = gevent|eventlet` +* Added support for gevent to replace eventlet. Going forward, the default will be gevent, and can be overriden in the conf file `system.concurrency_library = gevent|eventlet` @jzufelt #6372 * implemented zstandard compression for parameters and results. #5995 contributed by @guzzijones12 From 30b251c57c48b2a14582539e8d7da55ee3b68fe1 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Mon, 24 Aug 2026 10:44:05 -0600 Subject: [PATCH 19/22] convert the new merged RMQ updated code to use concurrency library --- st2actions/st2actions/cmd/scheduler.py | 7 ++-- ..._scheduler_shutdown_on_rabbitmq_failure.py | 32 +++++++++++-------- st2reactor/st2reactor/cmd/rulesengine.py | 10 +++--- ...ulesengine_shutdown_on_rabbitmq_failure.py | 16 +++++----- 4 files changed, 34 insertions(+), 31 deletions(-) diff --git a/st2actions/st2actions/cmd/scheduler.py b/st2actions/st2actions/cmd/scheduler.py index a2a0487237..f4d9fe6a65 100644 --- a/st2actions/st2actions/cmd/scheduler.py +++ b/st2actions/st2actions/cmd/scheduler.py @@ -29,6 +29,7 @@ from st2common.service_setup import teardown as common_teardown from st2common.service_setup import setup as common_setup from st2common.service_setup import deregister_service +from st2common.util import concurrency __all__ = ["main"] @@ -101,8 +102,6 @@ def _run_scheduler(): # Wait on both handler and entrypoint. If either fails, we want to shut down gracefully. # Poll the threads to detect when any of them fails - import eventlet - threads_to_monitor = [ (handler._main_thread, "handler_main"), (handler._cleanup_thread, "handler_cleanup"), @@ -125,7 +124,7 @@ def _run_scheduler(): # failure. for thread, name in dead_threads: try: - thread.wait() # Raises if the thread raised. + concurrency.wait(thread) # Raises if the thread raised. except Exception as e: LOG.error("Thread %s failed: %s", name, e) # Re-raise to let outer exception handler deal with shutdown @@ -138,7 +137,7 @@ def _run_scheduler(): return 0 # Sleep briefly to avoid tight loop and allow other greenlets to run - eventlet.sleep(0.1) + concurrency.sleep(0.1) except Exception as e: # If we caught an exception, it's already been logged and components shut down # Re-raise it so tests and monitoring can detect the failure diff --git a/st2actions/tests/unit/test_scheduler_shutdown_on_rabbitmq_failure.py b/st2actions/tests/unit/test_scheduler_shutdown_on_rabbitmq_failure.py index 0ed3a84296..8bee20562b 100644 --- a/st2actions/tests/unit/test_scheduler_shutdown_on_rabbitmq_failure.py +++ b/st2actions/tests/unit/test_scheduler_shutdown_on_rabbitmq_failure.py @@ -20,10 +20,10 @@ from __future__ import absolute_import -import eventlet import mock from kombu import exceptions as kombu_exceptions +from st2common.util import concurrency from st2tests.base import DbTestCase import st2tests.config as tests_config from st2actions.cmd import scheduler @@ -49,14 +49,14 @@ def test_scheduler_shuts_down_when_entrypoint_consumer_fails( """ Test that when the entrypoint consumer thread fails with KombuError after exhausting retry attempts, the scheduler: - 1. Detects the failure via eventlet.wait_all() + 1. Detects the failure via concurrency.wait() 2. Calls shutdown() on both handler and entrypoint 3. Re-raises the exception to exit the process """ # Create mock handler with threads that would run forever mock_handler = mock.MagicMock() - mock_handler_main_thread = eventlet.spawn(lambda: eventlet.sleep(1000)) - mock_handler_cleanup_thread = eventlet.spawn(lambda: eventlet.sleep(1000)) + mock_handler_main_thread = concurrency.spawn(lambda: concurrency.sleep(1000)) + mock_handler_cleanup_thread = concurrency.spawn(lambda: concurrency.sleep(1000)) mock_handler._main_thread = mock_handler_main_thread mock_handler._cleanup_thread = mock_handler_cleanup_thread mock_get_handler.return_value = mock_handler @@ -68,7 +68,7 @@ def test_scheduler_shuts_down_when_entrypoint_consumer_fails( def failing_consumer(): raise kombu_exceptions.OperationalError("[Errno 111] ECONNREFUSED") - mock_entrypoint_consumer_thread = eventlet.spawn(failing_consumer) + mock_entrypoint_consumer_thread = concurrency.spawn(failing_consumer) mock_entrypoint._consumer_thread = mock_entrypoint_consumer_thread mock_get_entrypoint.return_value = mock_entrypoint @@ -98,15 +98,17 @@ def test_scheduler_shuts_down_when_handler_main_thread_fails( def failing_main_thread(): raise RuntimeError("Handler main thread failed") - mock_handler_main_thread = eventlet.spawn(failing_main_thread) - mock_handler_cleanup_thread = eventlet.spawn(lambda: eventlet.sleep(1000)) + mock_handler_main_thread = concurrency.spawn(failing_main_thread) + mock_handler_cleanup_thread = concurrency.spawn(lambda: concurrency.sleep(1000)) mock_handler._main_thread = mock_handler_main_thread mock_handler._cleanup_thread = mock_handler_cleanup_thread mock_get_handler.return_value = mock_handler # Create mock entrypoint that would run forever mock_entrypoint = mock.MagicMock() - mock_entrypoint_consumer_thread = eventlet.spawn(lambda: eventlet.sleep(1000)) + mock_entrypoint_consumer_thread = concurrency.spawn( + lambda: concurrency.sleep(1000) + ) mock_entrypoint._consumer_thread = mock_entrypoint_consumer_thread mock_get_entrypoint.return_value = mock_entrypoint @@ -132,19 +134,21 @@ def test_scheduler_shuts_down_when_handler_cleanup_thread_fails( """ # Create mock handler with cleanup thread that fails mock_handler = mock.MagicMock() - mock_handler_main_thread = eventlet.spawn(lambda: eventlet.sleep(1000)) + mock_handler_main_thread = concurrency.spawn(lambda: concurrency.sleep(1000)) def failing_cleanup_thread(): raise RuntimeError("Handler cleanup thread failed") - mock_handler_cleanup_thread = eventlet.spawn(failing_cleanup_thread) + mock_handler_cleanup_thread = concurrency.spawn(failing_cleanup_thread) mock_handler._main_thread = mock_handler_main_thread mock_handler._cleanup_thread = mock_handler_cleanup_thread mock_get_handler.return_value = mock_handler # Create mock entrypoint that would run forever mock_entrypoint = mock.MagicMock() - mock_entrypoint_consumer_thread = eventlet.spawn(lambda: eventlet.sleep(1000)) + mock_entrypoint_consumer_thread = concurrency.spawn( + lambda: concurrency.sleep(1000) + ) mock_entrypoint._consumer_thread = mock_entrypoint_consumer_thread mock_get_entrypoint.return_value = mock_entrypoint @@ -170,8 +174,8 @@ def test_scheduler_connection_error_propagates( """ # Create mock handler with threads that would run forever mock_handler = mock.MagicMock() - mock_handler_main_thread = eventlet.spawn(lambda: eventlet.sleep(1000)) - mock_handler_cleanup_thread = eventlet.spawn(lambda: eventlet.sleep(1000)) + mock_handler_main_thread = concurrency.spawn(lambda: concurrency.sleep(1000)) + mock_handler_cleanup_thread = concurrency.spawn(lambda: concurrency.sleep(1000)) mock_handler._main_thread = mock_handler_main_thread mock_handler._cleanup_thread = mock_handler_cleanup_thread mock_get_handler.return_value = mock_handler @@ -182,7 +186,7 @@ def test_scheduler_connection_error_propagates( def failing_consumer(): raise kombu_exceptions.ConnectionError("Connection lost") - mock_entrypoint_consumer_thread = eventlet.spawn(failing_consumer) + mock_entrypoint_consumer_thread = concurrency.spawn(failing_consumer) mock_entrypoint._consumer_thread = mock_entrypoint_consumer_thread mock_get_entrypoint.return_value = mock_entrypoint diff --git a/st2reactor/st2reactor/cmd/rulesengine.py b/st2reactor/st2reactor/cmd/rulesengine.py index cefe58f584..9ee98556ec 100644 --- a/st2reactor/st2reactor/cmd/rulesengine.py +++ b/st2reactor/st2reactor/cmd/rulesengine.py @@ -27,10 +27,10 @@ from st2common.service_setup import setup as common_setup from st2common.service_setup import teardown as common_teardown from st2common.service_setup import deregister_service +from st2common.util import concurrency from st2reactor.rules import config from st2reactor.rules import worker - LOGGER_NAME = get_logger_name_for_module(sys.modules[__name__]) LOG = logging.getLogger(LOGGER_NAME) RULESENGINE = "rulesengine" @@ -64,8 +64,6 @@ def _run_worker(): rules_engine_worker.start() # Monitor the worker thread - if it dies/fails, we need to exit cleanly - import eventlet - # Poll the worker thread to detect failures while True: if ( @@ -74,7 +72,9 @@ def _run_worker(): ): # Thread died - try to get the exception if it raised one try: - rules_engine_worker._consumer_thread.wait() # This will raise if thread raised + concurrency.wait( + rules_engine_worker._consumer_thread + ) # This will raise if thread raised except Exception as e: LOG.error("RulesEngine worker thread failed: %s", e) raise @@ -83,7 +83,7 @@ def _run_worker(): return 0 # Sleep briefly to avoid tight loop - eventlet.sleep(0.1) + concurrency.sleep(0.1) except (KeyboardInterrupt, SystemExit): LOG.info("(PID=%s) RulesEngine stopped.", os.getpid()) deregister_service(RULESENGINE) diff --git a/st2reactor/tests/unit/test_rulesengine_shutdown_on_rabbitmq_failure.py b/st2reactor/tests/unit/test_rulesengine_shutdown_on_rabbitmq_failure.py index 04e510e2c4..bf9ff1e1ea 100644 --- a/st2reactor/tests/unit/test_rulesengine_shutdown_on_rabbitmq_failure.py +++ b/st2reactor/tests/unit/test_rulesengine_shutdown_on_rabbitmq_failure.py @@ -19,11 +19,11 @@ from __future__ import absolute_import -import eventlet import mock from kombu.exceptions import OperationalError as KombuOperationalError +from st2common.util import concurrency from st2reactor.cmd.rulesengine import _run_worker from st2reactor.rules.worker import TriggerInstanceDispatcher from st2tests.base import CleanDbTestCase @@ -47,11 +47,11 @@ def test_rulesengine_connection_error_propagates(self, mock_get_connection): mock_get_connection.side_effect = KombuOperationalError("Connection refused") # Run the worker in a greenthread - run_thread = eventlet.spawn(_run_worker) + run_thread = concurrency.spawn(_run_worker) # The worker should raise the connection error with self.assertRaises(KombuOperationalError) as cm: - run_thread.wait() + concurrency.wait(run_thread) self.assertIn("Connection refused", str(cm.exception)) @@ -67,17 +67,17 @@ def test_rulesengine_shuts_down_when_worker_thread_fails( def mock_start_that_fails(): # Simulate the worker thread starting but then failing - eventlet.sleep(0.1) + concurrency.sleep(0.1) raise RuntimeError("Worker thread failed") mock_start.side_effect = mock_start_that_fails # Run the worker - run_thread = eventlet.spawn(_run_worker) + run_thread = concurrency.spawn(_run_worker) # Should raise the RuntimeError from the worker thread with self.assertRaises(RuntimeError) as cm: - run_thread.wait() + concurrency.wait(run_thread) self.assertIn("Worker thread failed", str(cm.exception)) @@ -98,10 +98,10 @@ def test_rulesengine_handles_connection_retry_exhaustion( "Failed to connect after 10 attempts" ) - run_thread = eventlet.spawn(_run_worker) + run_thread = concurrency.spawn(_run_worker) # Should raise the connection error with self.assertRaises(KombuOperationalError) as cm: - run_thread.wait() + concurrency.wait(run_thread) self.assertIn("Failed to connect", str(cm.exception)) From bafbec025491e38f050d4d847495bb5173632484 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Fri, 28 Aug 2026 14:52:25 -0600 Subject: [PATCH 20/22] Trigger CI rebuild From fd41090c2cd762daf515f0d64ac933e74752e707 Mon Sep 17 00:00:00 2001 From: "st2stanley[bot]" <7807286+st2stanley@users.noreply.github.com> Date: Fri, 28 Aug 2026 21:27:30 +0000 Subject: [PATCH 21/22] pants generate-lockfiles: st2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __________________________________________________________________ Lockfile diff: lockfiles/st2.lock [st2] __________________________________________________________________ == Upgraded dependencies == ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ annotated-types 0.7.0 --> 0.8.0 argcomplete 3.7.0 --> 3.7.2 certifi 2026.6.17 --> 2026.7.22 cffi 2.1.0 --> 2.1.1 chardet 7.4.3 --> 7.6.0 charset-normalizer 3.4.9 --> 3.5.1 click 8.4.2 --> 8.5.0 cryptography 49.0.0 --> 50.0.1 eventlet 0.41.0 --> 0.41.2 filelock 3.29.7 --> 3.32.4 gitpython 3.1.51 --> 3.1.61 greenlet 3.5.3 --> 3.5.5 idna 3.18 --> 3.19 importlib-metadata 9.0.0 --> 9.0.1 msgpack 1.2.1 --> 1.2.2 orjson 3.11.9 --> 3.12.0 packaging 26.2 --> 26.3 pika 1.4.1 --> 1.4.4 pip 26.1.2 --> 26.2.1 platformdirs 4.10.0 --> 4.11.5 prance 25.4.8.0 --> 26.7.19.0 prompt-toolkit 3.0.52 --> 3.0.53 pydantic 2.13.4 --> 2.13.5 pydantic-core 2.46.4 --> 2.46.5 pydantic-settings 2.14.2 --> 2.15.0 pygments 2.20.0 --> 2.21.0 pyspnego 0.12.1 --> 0.12.2 pytest-forked 1.6.0 --> 1.7.5 python-discovery 1.4.4 --> 1.6.0 python-dotenv 1.2.2 --> 1.2.3 python-json-logger 4.1.0 --> 4.2.0 pytz 2026.2 --> 2026.3.post1 redis 8.0.1 --> 8.1.0 simplejson 4.1.1 --> 4.1.2 soupsieve 2.8.4 --> 2.9.2 typing-inspection 0.4.2 --> 0.4.4 wcwidth 0.8.2 --> 0.8.3 webob 1.8.10 --> 1.8.11 wheel 0.47.0 --> 0.48.0 wrapt 2.2.2 --> 2.3.0 __________________________________________________________________ == Added dependencies == ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ gevent 26.8.0 zope-event 6.2 zope-interface 8.6 --- fixed-requirements.txt | 72 +- lockfiles/st2.lock | 1859 ++++++++++++++++++----------------- requirements.txt | 70 +- st2actions/requirements.txt | 18 +- st2api/requirements.txt | 12 +- st2auth/requirements.txt | 8 +- st2client/requirements.txt | 28 +- st2common/requirements.txt | 44 +- st2reactor/requirements.txt | 8 +- st2stream/requirements.txt | 12 +- st2tests/requirements.txt | 4 +- test-requirements.txt | 16 +- 12 files changed, 1089 insertions(+), 1062 deletions(-) diff --git a/fixed-requirements.txt b/fixed-requirements.txt index 246ddce30f..d582c1e1a5 100644 --- a/fixed-requirements.txt +++ b/fixed-requirements.txt @@ -7,86 +7,86 @@ # ---------------------------------------------------------------------- # Note: amqp is used by kombu amqp==5.3.1 -apscheduler==3.11.0 -chardet==5.2.0 -cffi==1.17.1 -cryptography==43.0.3 -eventlet==0.40.4 +apscheduler==3.11.3 +chardet==7.6.0 +cffi==2.1.1 +cryptography==50.0.1 +eventlet==0.41.2 # Note: installs gitpython==3.1.37 (security fixed) under py3.8 and gitpython==3.1.18 (latest available, vulnerable) under py3.6 -gitpython==3.1.45 +gitpython==3.1.61 # Needed by gitpython, old versions used to bundle it gitdb==4.0.12 -gevent==25.9.1 +gevent==26.8.0 # Note: greenlet is used by eventlet & gevent (3.2.2 is the minimum common version) -greenlet==3.2.2 -gunicorn==23.0.0 +greenlet==3.5.5 +gunicorn==25.3.0 # importlib backport replaces setuptool's pkg_resource. importlib_resources==6.5.2 importlib_metadata==9.0.0 jsonpath-rw==1.4.0 jsonschema==4.26.0 # openapi-spec-validator pinned to align with st2's jsonschema pinning constraint -openapi-spec-validator==0.7.2 -kombu==5.5.4 +openapi-spec-validator==0.9.0 +kombu==5.6.2 lockfile==0.12.2 -mongoengine==0.29.1 -networkx==3.1 +mongoengine==0.29.3 +networkx==3.3 # jsonpath-rw is the only thing that depends on decorator (a transitive dep) -decorator==5.2.1 +decorator==5.3.1 # 202403: Bump oslo.config for py3.10 support. oslo.config==9.6.0 oslo.utils==7.3.0 paramiko==3.5.1 # 202403: bump to 3.0.43 for py3.10 support -prompt-toolkit==3.0.52 +prompt-toolkit==3.0.53 pymongo==4.6.3 -pyparsing==3.1.4 -zstandard==0.23.0 +pyparsing==3.3.2 +zstandard==0.25.0 # pyOpenSSL is a candidate for removal and is not in lockfiles/st2.lock since it is not used. # pyOpenSSL 25.1.0 supports cryptography>=41.0.5,<46 pyOpenSSL<25.2 # 202403: switch from python-editor to editor for py3.10 support -editor==1.6.6 +editor==1.8.0 # editor dependency, required here for inclusion in st2client setup.py -pygments==2.19.2 +pygments==2.21.0 python-keyczar==0.716 -pytz==2025.2 +pytz==2026.3.post1 pywinrm==0.5.0 pyyaml==6.0.3 -redis==6.1.1 -requests==2.32.4 +redis==8.1.0 +requests==2.34.2 # urllib3 is a transitive dep -urllib3==2.2.3 +urllib3==2.7.0 retrying==1.4.2 routes==2.5.1 semver==3.0.4 six==1.17.0 argparse==1.4.0 -argcomplete==3.6.2 -prettytable==3.11.0 +argcomplete==3.7.2 +prettytable==3.18.0 # 20250909 virtualenv 20.34.0 requires typing-extension>=4.13.2 typing-extensions>=4.13.2; python_version < "3.11" # NOTE: sseclient has various issues which sometimes hang the connection for a long time, etc. -sseclient-py==1.8.0 -stevedore==5.3.0 -tenacity==9.0.0 -tooz==6.3.0 +sseclient-py==1.9.0 +stevedore==5.8.0 +tenacity==9.1.4 +tooz==8.1.0 # Note: virtualenv embeds wheels for pip, and setuptools. https://github.com/pypa/virtualenv/src/virtualenv/seed/wheels/embed # virtualenv==21.5.1 embeds pip==26.1.2 setuptools==82.0.1 # lockfiles/st2.lock has pip==25.0.1 wheel==0.45.1 setuptools==75.3.2 -pip==26.1.2 +pip==26.2.1 setuptools==82.0.1 -webob==1.8.9 -webtest==3.0.1 +webob==1.8.11 +webtest==3.0.7 zake==0.2.2 # test requirements below bcrypt==5.0.0 jinja2==3.1.6 mock==5.2.0 pytest==7.0.1 -psutil==7.1.0 +psutil==7.2.2 python-dateutil==2.9.0.post0 python-statsd==2.1.0 -orjson==3.10.15 -zipp==3.20.2 -pynacl==1.6.0 +orjson==3.12.0 +zipp==4.1.0 +pynacl==1.6.2 diff --git a/lockfiles/st2.lock b/lockfiles/st2.lock index e76a34b7de..6e58a87720 100644 --- a/lockfiles/st2.lock +++ b/lockfiles/st2.lock @@ -134,21 +134,19 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", - "url": "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl" + "hash": "f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", + "url": "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", - "url": "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz" + "hash": "13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", + "url": "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz" } ], "project_name": "annotated-types", - "requires_dists": [ - "typing-extensions>=4.0.0; python_version < \"3.9\"" - ], - "requires_python": ">=3.8", - "version": "0.7.0" + "requires_dists": [], + "requires_python": ">=3.10", + "version": "0.8.0" }, { "artifacts": [ @@ -196,13 +194,13 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "d8f0f22d2a8a7caa383be1e22b6caf1ecaf0ebd10d8f83cc125e36540c95830c", - "url": "https://files.pythonhosted.org/packages/12/f6/5b8ec087cd9cfa9449491ec83f76fb6b7006b4dff57d2ba8aaab330fe8e4/argcomplete-3.7.0-py3-none-any.whl" + "hash": "6029205678bdd9c1c728a155f5f9ecf5812393f969eef58807641a2bc2aa5b19", + "url": "https://files.pythonhosted.org/packages/46/bd/551ee6af426af84ca33e02622be722925c196608e9127d731ef17c47f06e/argcomplete-3.7.2-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "afde224f753f874807b1dc1414e883ab8fe0cda9c04807b6047dcb8e1ac23913", - "url": "https://files.pythonhosted.org/packages/95/c0/c8e94135e66fabf89a120d9b4b123fe6993506beca6c1938a74c24cfa5fd/argcomplete-3.7.0.tar.gz" + "hash": "aad8b69a0b9969edb62db0d1752354c0d50717b10e0cbb00e2a958381b9fc6b9", + "url": "https://files.pythonhosted.org/packages/87/6f/5a73f04007ca950701765949209f068da628bd11f9c2da287278ce91e0ee/argcomplete-3.7.2.tar.gz" } ], "project_name": "argcomplete", @@ -213,8 +211,8 @@ "ruff; extra == \"test\"", "wheel; extra == \"test\"" ], - "requires_python": ">=3.8", - "version": "3.7.0" + "requires_python": ">=3.10", + "version": "3.7.2" }, { "artifacts": [ @@ -492,171 +490,171 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", - "url": "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl" + "hash": "62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", + "url": "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", - "url": "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz" + "hash": "741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", + "url": "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz" } ], "project_name": "certifi", "requires_dists": [], "requires_python": ">=3.7", - "version": "2026.6.17" + "version": "2026.7.22" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "379de10ce1ba048b1448599d1b37b24caee16309d1ac98d3982fc997f768700b", - "url": "https://files.pythonhosted.org/packages/79/99/0d0fd37f055224085f42bbb2c022d002e17dde4a97972822327b07d84101/cffi-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl" + "hash": "210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", + "url": "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "89095c1968b4ba8285840e131bf2891b09ae137fe2146905acae0354fbce1b5e", - "url": "https://files.pythonhosted.org/packages/04/8c/b925975448cf20634a9fbd5efceb807219db452653648d2897c0989cab2d/cffi-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl" + "hash": "3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659", + "url": "https://files.pythonhosted.org/packages/09/b8/eefc0e06913b70aa153bf74c946094a18f58fd4aff11b7f372bfdfdca050/cffi-2.1.1-cp310-cp310-musllinux_1_2_i686.whl" }, { "algorithm": "sha256", - "hash": "b6422532152adf4e59b110cb2808cee7a033800952f5c036b4af047ee43199e7", - "url": "https://files.pythonhosted.org/packages/1b/dc/5620cf930688be01f2d673804291de757a934c90b946dbdc3d84130c2ea4/cffi-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" + "hash": "c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", + "url": "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl" }, { "algorithm": "sha256", - "hash": "df2b82571a1b30f58a87bf4e5a9e78d2b1eff6c6ce8fd3aa3757221f93f0863f", - "url": "https://files.pythonhosted.org/packages/1e/85/990925db5df586ec90beb97529c853497e7f85ba0234830447faf41c3057/cffi-2.1.0-cp312-cp312-macosx_10_15_x86_64.whl" + "hash": "42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004", + "url": "https://files.pythonhosted.org/packages/17/0b/ba385d8ccedf926c3cd06e8e2f327027da5afe5f0eb30f1f7bc43ac55125/cffi-2.1.1-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl" }, { "algorithm": "sha256", - "hash": "88023dfe18799507b73f1dbb0d14326a17465de1bc9c9c7655c22845e9ddc3a2", - "url": "https://files.pythonhosted.org/packages/22/d7/1a74539db16d8bfd839ff1515948948efbb162e574650fd3d846896eea95/cffi-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" + "hash": "75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9", + "url": "https://files.pythonhosted.org/packages/18/d4/03026f0c850cbbaa9030750490225b4a7f4d524ea4df72c3cc740a90f4ef/cffi-2.1.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl" }, { "algorithm": "sha256", - "hash": "7762faa47e8ff7eb80bd261d9a7d8eea2d8baa69de5e95b70c1f338bbe712f02", - "url": "https://files.pythonhosted.org/packages/22/f0/a2fc43084c0433caf7f461bccc013e28f848d04ee1c5ed7fce71423cf4d9/cffi-2.1.0-cp311-cp311-musllinux_1_2_i686.whl" + "hash": "7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd", + "url": "https://files.pythonhosted.org/packages/28/f7/dabe6da2466ecbd82dc62e7342dc6b1065dad990c06f00f0ede9ebf2a0ed/cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "4d433a51f1870e43a13b6732f92aaf540ff77c2015097c78556f75a2d6c030e0", - "url": "https://files.pythonhosted.org/packages/38/66/04781a77b411f0bb5b234d62c1814754ab75ebe455ccff1b08e8d7aae98f/cffi-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" + "hash": "9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98", + "url": "https://files.pythonhosted.org/packages/37/15/180e0dab27b9312c7479003d14c9e547634b7dcb934e2cc4650e1b131a7a/cffi-2.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "a016194dbe13d14ee9556e734b772d8d67b947092b268d757fd4290e3ba2dfc2", - "url": "https://files.pythonhosted.org/packages/3b/30/c806937ed5e4c2c7ac30d9d6b76b5dc57ff8b75d83800d9bb11a8253cf2a/cffi-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" + "hash": "68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", + "url": "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" }, { "algorithm": "sha256", - "hash": "c5f5df567f6eb216de69be06ce55c8b714090fae02b18a3b40da8163b8c5fa9c", - "url": "https://files.pythonhosted.org/packages/41/aa/3c1409cdd26094efacd1c36c66e0a6eb9d4296e4fd4f9901b8b2042f4323/cffi-2.1.0-cp310-cp310-musllinux_1_2_i686.whl" + "hash": "f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", + "url": "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "19c54ac121cad98450b4896fa9a43ee0180d57bc4bc911a33db6cab1efab6cd3", - "url": "https://files.pythonhosted.org/packages/45/ca/f91641185cdd90c36d317a9dc7f85e88ef8682d8b300977baff5e23c35d8/cffi-2.1.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl" + "hash": "811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", + "url": "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl" }, { "algorithm": "sha256", - "hash": "78474632761faa0fb96f30b1c928c84ebcf68713cbb80d15bab09dfe61640fde", - "url": "https://files.pythonhosted.org/packages/4b/92/e7bb136ad6b5352603732cf907ef862ca103f20f2031c1735a46300c20c9/cffi-2.1.0-cp312-cp312-macosx_11_0_arm64.whl" + "hash": "3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", + "url": "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl" }, { "algorithm": "sha256", - "hash": "46b1c8db8f6122420f32d02fffb924c2fe9bc772d228c7c711748fff56aabb2b", - "url": "https://files.pythonhosted.org/packages/4b/a4/77b53abbf7a1e0beb9637edbef2a94d15f9c822f591e85d439ffd91519a6/cffi-2.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl" + "hash": "4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", + "url": "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", - "url": "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz" + "hash": "1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9", + "url": "https://files.pythonhosted.org/packages/6f/13/4e56852824a03cdf68523a35686f1c28eacd4bd30a7b0a78e682e6e6e1d3/cffi-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "d9fafc5aa2e2a39aaf7f8cc0c1f044a9b07fca12e558dca53a3cc5c654ad67a7", - "url": "https://files.pythonhosted.org/packages/58/0c/f528df19cc94b675087324d4760d9e6d5bfae97d6217aa4fac43de4f5fcc/cffi-2.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl" + "hash": "c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12", + "url": "https://files.pythonhosted.org/packages/70/d2/16d99a0c4948febc0ebd133a13b2f688ff7f8cb04da971e1128872ce0c03/cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl" }, { "algorithm": "sha256", - "hash": "1e9f50d192a3e525b15a75ab5114e442d83d657b7ec29182a991bc9a88fd3a66", - "url": "https://files.pythonhosted.org/packages/62/f2/c9522a81c32132799a1972c39f5c5f8b4c8b9f00488a23feaa6c06f07741/cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" + "hash": "194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6", + "url": "https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" }, { "algorithm": "sha256", - "hash": "30b65779d598c370374fefabf138d456fd6f3216bfa7bedfab1ba82025b0cd93", - "url": "https://files.pythonhosted.org/packages/65/68/9f3ef890cf3c6ab97bd531c5677f67613d302165d16f8142b2811782a614/cffi-2.1.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl" + "hash": "ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b", + "url": "https://files.pythonhosted.org/packages/94/1a/4b2f7c92293ba05cbd4a9a1b28faaf0326272d9488e6354657571c48a7aa/cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "164bff1657b2a74f0b6d54e11c9b375bc97b931f2ca9c43fcf875838da1570dd", - "url": "https://files.pythonhosted.org/packages/69/aa/24580a278de21fd7322635556334d9b535f1cbc00b0a3919447cdf464c65/cffi-2.1.0-cp310-cp310-macosx_11_0_arm64.whl" + "hash": "dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", + "url": "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz" }, { "algorithm": "sha256", - "hash": "98fff996e983a36d3aa2eca83af40c5821202e7e6f32d13ae94e3d2286f10cfe", - "url": "https://files.pythonhosted.org/packages/6e/28/bd53988b9833e8f8ad539d26f4c07a6b3f6bcb1e9e02e7ca038250b3428d/cffi-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl" + "hash": "208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", + "url": "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "c941bb58d5a6e1c3892d86e42927ed6c180302f07e6d395d08c416e594b98b46", - "url": "https://files.pythonhosted.org/packages/88/a9/02cae418ec4beb282ace11958d9d4737793439d561fadc7e6d56f2e2b354/cffi-2.1.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl" + "hash": "5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9", + "url": "https://files.pythonhosted.org/packages/a3/b9/0f2e58b2cefa33255bff36935d42b13180fe559bba82596540eb404bde7d/cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" }, { "algorithm": "sha256", - "hash": "9d8272c0e483b024e1b9ad029821470ed8ec65631dbd90217469da0e7cd89f1c", - "url": "https://files.pythonhosted.org/packages/a2/a5/d4fe77b589e5e82d43ebc809bf2e6474afe8e48e32ea050b9357645b6471/cffi-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl" + "hash": "a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af", + "url": "https://files.pythonhosted.org/packages/a5/a7/a67c733254d6e7373f7822f8082d8d6beade791e0cf12a7611f376fa61c7/cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl" }, { "algorithm": "sha256", - "hash": "a5781494d4d400a3f47f8f1da94b324f6e6b440a53387774002890a2a2f4b50f", - "url": "https://files.pythonhosted.org/packages/ba/b0/e131a9c41f10607926278453d9596163594fe1c4ebc46efe3b5e5b34eb84/cffi-2.1.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl" + "hash": "6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990", + "url": "https://files.pythonhosted.org/packages/a7/92/500760486c8baab49a7a8a58ba7fc3355ec3974b454b8a09e528efde9e1d/cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "b65f590ef2a44640f9a05dbb548a429b4ade77913ce683ac8b1480777658a6c0", - "url": "https://files.pythonhosted.org/packages/c0/e9/6d7724983b3d5a0908dbf74f64038ade77c18646ff6636ec7894fd392ce1/cffi-2.1.0-cp310-cp310-macosx_10_15_x86_64.whl" + "hash": "154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0", + "url": "https://files.pythonhosted.org/packages/ad/5a/4707a0dc1f203f5dde5a907b0d4e3c25d71120241048bd5bc6f1bb9d4e71/cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl" }, { "algorithm": "sha256", - "hash": "5972433ad71a9e46516584ef60a0fda12d9dc459938d1539c3ddecf9bdc1368d", - "url": "https://files.pythonhosted.org/packages/c3/c0/d1ec30ffb370f748f2fb54425972bfef9871e0132e82fb589c46b6676049/cffi-2.1.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl" + "hash": "3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813", + "url": "https://files.pythonhosted.org/packages/ad/66/c19feabb28485b6e0bbaaafa90837a1ef5d302e90f2178bd33f17a49879b/cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" }, { "algorithm": "sha256", - "hash": "3d7f118b5adbfdfead90c25822690b02bc8074fba949bb7858bec4ebd55adb43", - "url": "https://files.pythonhosted.org/packages/d0/9a/bb1d5ed9c3fcae158e9f6391bf309c95d98c2ac37ed56573228471d0af5e/cffi-2.1.0-cp310-cp310-musllinux_1_2_aarch64.whl" + "hash": "5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf", + "url": "https://files.pythonhosted.org/packages/b0/ae/679bf47e73fd77b352171727f07de559a003f14de5d02b904a6ec1fa73ca/cffi-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "02cb7ff33ded4f1532476731f89ede53e2e488a8e6205515a82144246ffa7dcc", - "url": "https://files.pythonhosted.org/packages/d3/67/85c89a59ba36a671e79638f44d466749f08179266a57e4f2ffdf92174072/cffi-2.1.0-cp311-cp311-macosx_10_15_x86_64.whl" + "hash": "c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", + "url": "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" }, { "algorithm": "sha256", - "hash": "f5bce581e6b8c235e566a14768a943b172ada3ed73537bb0c0be1edee312d4e7", - "url": "https://files.pythonhosted.org/packages/ea/dd/e3b0baa2d3d6a857ac72b7efbf18e32e487c9cdafcc13049ad765495b15e/cffi-2.1.0-cp311-cp311-macosx_11_0_arm64.whl" + "hash": "f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa", + "url": "https://files.pythonhosted.org/packages/b4/c6/ab025d75d2c26c19b087c0124e75ee31cb65032f4fe345d356d8c507ab97/cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "0a96b74cda968eebbad56d973efe5098974f0a9fb323865bf99ea1fd24e3e64c", - "url": "https://files.pythonhosted.org/packages/ec/d1/9a5b7169499e8e8d8e636de70b97ac7c9447104d2ff1a2cd94790cea5162/cffi-2.1.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl" + "hash": "baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be", + "url": "https://files.pythonhosted.org/packages/b6/d2/2cde336b375f55c76ca670f0be3978cc048e31e24f3b4d7ce8473150a388/cffi-2.1.1-cp310-cp310-macosx_10_15_x86_64.whl" }, { "algorithm": "sha256", - "hash": "03e9810d18c646077e501f661b682fbf5dee4676048527ca3cffe66faa9960dd", - "url": "https://files.pythonhosted.org/packages/f9/cf/398272b8bbfd58aa314fda5a7f1cdbb26d1d78ae324a11211521315dd1f0/cffi-2.1.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl" + "hash": "398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1", + "url": "https://files.pythonhosted.org/packages/cd/95/31b535a9f0220ae9f357de4a08d57ce89cb417653c2fd9f075f50822a388/cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "11b3fb55f4f8ad92274ed26705f65d8f91457de71f5380061eb6d125a768fecd", - "url": "https://files.pythonhosted.org/packages/fa/75/74dfb7c3fc6ebbd408038476bd4c1d7e925c62614e7b9c534ecc34218288/cffi-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl" + "hash": "df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a", + "url": "https://files.pythonhosted.org/packages/ce/87/616202d8e51342c07d2534c510111c4cc37201775ce8f60802c9335d1edd/cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl" }, { "algorithm": "sha256", - "hash": "aa7a1b53a2a4452ada2d1b5dade9960b2522f1e61293a811a077439e39029565", - "url": "https://files.pythonhosted.org/packages/fb/d2/4398416cd699b35167947c6e22aca52c47e69ad5695073c9f1f2c52e04aa/cffi-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" + "hash": "34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632", + "url": "https://files.pythonhosted.org/packages/f7/a4/4399daaf8f7dfee9d7c3327fdb0426ee041cc63edc358b93911ceb2bfc7a/cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" } ], "project_name": "cffi", @@ -664,268 +662,378 @@ "pycparser; implementation_name != \"PyPy\"" ], "requires_python": ">=3.10", - "version": "2.1.0" + "version": "2.1.1" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "1173b74051570cf08099d7429d92e4882d375ad4217f92a6e5240ccfb26f231e", - "url": "https://files.pythonhosted.org/packages/8c/6c/0a40afdb50a0fe041ab95553b835a8160b6cf0e81edf2ae2fe9f5224cbf9/chardet-7.4.3-py3-none-any.whl" + "hash": "4076d795897ce45239825956a1334e134322ecc4bfe84dbb12acd5390de0fbc1", + "url": "https://files.pythonhosted.org/packages/cf/6e/5a0b348fa4cd7847567a28c6e697ccf58391960bfd13a6e7473ee23ca2f2/chardet-7.6.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "7005c88da26fd95d8abb8acbe6281d833e9a9181b03cf49b4546c4555389bd97", - "url": "https://files.pythonhosted.org/packages/14/4b/d3c79495dee4831b8bebca2790e72cb90f0c5849c940570a7c7e5b70b952/chardet-7.4.3-cp311-cp311-macosx_11_0_arm64.whl" + "hash": "aa03322e07ac08d520ec50bb50c73143d0892d1adc067d4c5e58f4ef4b2363a8", + "url": "https://files.pythonhosted.org/packages/03/25/9c8db4f951e974a4db5558d9eea62e1fd5b5889c9d0ad0315eed67c5cdb3/chardet-7.6.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "3990fffcc6a6045f2234ab72752ad037e3b2d48c72037f244d42738db397eb75", - "url": "https://files.pythonhosted.org/packages/18/ef/ea4edec8c87f7e6eda02673acc68fe48725e564fc5a1865782efb53d5598/chardet-7.4.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + "hash": "6424512f576fa7e88b7431d38a42d57552c8f717465a975fc42e497cd280d833", + "url": "https://files.pythonhosted.org/packages/11/2e/d8634bee23a07bf512512ddf6218a68e47f045ae061c0cc80657ed79dcc0/chardet-7.6.0-cp311-cp311-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "25a862cddc6a9ac07023e808aedd297115345fbaabc2690479481ddc0f980e09", - "url": "https://files.pythonhosted.org/packages/19/52/505c207f334d51e937cbaa27ff95776e16e2d120e13cbe491cd7b3a70b50/chardet-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl" + "hash": "7b586cab9e9072dddd89bc2bd27ee72808d0c84ec73695fe6ec0f3c46b057c65", + "url": "https://files.pythonhosted.org/packages/20/38/b32daa70f8bacd47e515ec9f85d571c87f31ff1c43c95aea7780eaefe7fc/chardet-7.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "cc1d4eb92a4ec1c2df3b490836ffa46922e599d34ce0bb75cf41fd2bf6303d56", - "url": "https://files.pythonhosted.org/packages/19/b6/9df434a8eeba2e6628c465a1dfa31034228ef79b26f76f46278f4ef7e49d/chardet-7.4.3.tar.gz" + "hash": "a12023d48d0e207791c01161d03cb3c0d85c6a15f345eb9d3d56063a63d1e40f", + "url": "https://files.pythonhosted.org/packages/44/99/934fb862d102c8756008597f4398323f32cef329f16e87fbb3bf76d4f4be/chardet-7.6.0-cp312-cp312-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "626f00299ad62dfe937058a09572beed442ccc7b58f87aa667949b20fd3db235", - "url": "https://files.pythonhosted.org/packages/36/21/edb36ad5dfa48d7f8eed97ab43931ecdaa8c15166c21b1d614967e49d681/chardet-7.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "284136186ff90735f901ed0a1c6d41e7af67c666841cc0eceb58482a21b7056c", + "url": "https://files.pythonhosted.org/packages/55/95/bd6d59026638cec47dace85858171fbecadd2f9e58cb2b973dc515aa790c/chardet-7.6.0-cp311-cp311-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "75d3c65cc16bddf40b8da1fd25ba84fca5f8070f2b14e86083653c1c85aee971", - "url": "https://files.pythonhosted.org/packages/61/33/29de185079e6675c3f375546e30a559b7ddc75ce972f18d6e566cd9ea4eb/chardet-7.4.3-cp312-cp312-macosx_10_13_x86_64.whl" + "hash": "cf6d08c2373b7772a558d141f9e8cee53fe1d222341bac612e4d558b04995f73", + "url": "https://files.pythonhosted.org/packages/56/1d/49f13052b74303bab2789d098063cbd19758217949ea54ffa216b6098cb3/chardet-7.6.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "c0c79b13c9908ac7dfe0a74116ebc9a0f28b2319d23c32f3dfcdfbe1279c7eaf", - "url": "https://files.pythonhosted.org/packages/6b/1b/7f73766c119a1344eb69e31890ede7c5825ce03d69a9d29292d1bd1cfa1b/chardet-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl" + "hash": "19fea52164e6e00f2a21ed418f42e4b0162a09199274c86d07ad3efd661317c4", + "url": "https://files.pythonhosted.org/packages/6f/62/64da80dad0c804e743b4156f379183578f1e33918856ae928dc9248a6002/chardet-7.6.0-cp312-cp312-macosx_10_13_x86_64.whl" }, { "algorithm": "sha256", - "hash": "93c45e116dd51b66226a53ade3f9f635e870de5399b90e00ce45dcc311093bf4", - "url": "https://files.pythonhosted.org/packages/6c/63/0f43e3acf2c436fdb32a0f904aeb03a2904d2126eed34a042a194d235926/chardet-7.4.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + "hash": "249993b88ac7a58cad2781acea8f379152a28a719c9b401d614898c63a8c83da", + "url": "https://files.pythonhosted.org/packages/71/e9/b04e0ec576a77e79fe37279a9a5d5b1ae752d365e43df2eca0d0eee4cea5/chardet-7.6.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "cfb54563fe5f130da17c44c6a4e2e8052ba628e5ab4eab7ef8190f736f0f8f72", - "url": "https://files.pythonhosted.org/packages/87/23/e31c8ad33aa448f0845fd58af5fc22da1626407616d09df4973b2b34f477/chardet-7.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + "hash": "0ad9bc6dab4f338673353fa3f0dc96122f559aaf746087408106e2fcbf132fe8", + "url": "https://files.pythonhosted.org/packages/76/1b/59eb88a78d8f5855c27c25788088df82834ad067f3dddaf4d86ef03cf2d3/chardet-7.6.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "5d2879598bc220689e8ce509fe9c3f37ad2fca53a36be9c9bd91abdd91dd364f", - "url": "https://files.pythonhosted.org/packages/87/2e/e1ee6a77abf3782c00e05b89c4d4328c8353bf9500661c4348df1dd68614/chardet-7.4.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + "hash": "2cf0adaca8b1c4bacfade9d0a1e4f8f70b1bb122833d6f07ab90e3adc84eb13a", + "url": "https://files.pythonhosted.org/packages/7d/a2/c4d99299e9ce7fad561f8bb56babbbbdd3bb6b4fbd7c0ec674c1dbdd2cc5/chardet-7.6.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "bba8bea1b28d927b3e99e47deafe53658d34497c0a891d95ff1ba8ff6663f01c", - "url": "https://files.pythonhosted.org/packages/8b/02/b677c8203d34dad6c2af48287bb1f8c5dff63db2094636fbe634b555b7fb/chardet-7.4.3-cp310-cp310-macosx_11_0_arm64.whl" + "hash": "5e9b31b9ae93872d66439b046a1e08c2ea99791f3c254dce1e2633e395c5587c", + "url": "https://files.pythonhosted.org/packages/7f/4a/60ed03656b28c1f4d378bc3cfe8a6cdda62c7c289398f925610fbbabfd00/chardet-7.6.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "29af5999f654e8729d251f1724a62b538b1262d9292cccaefddf8a02aae1ef6a", - "url": "https://files.pythonhosted.org/packages/9c/2f/4c5af01fd1a7506a1d5375403d68925eac70289229492db5aa68b58103d8/chardet-7.4.3-cp312-cp312-macosx_11_0_arm64.whl" + "hash": "cbaca8f563a9de07ab1a53157dba93802e54c26afe3339892afcc7c59ea4ef1b", + "url": "https://files.pythonhosted.org/packages/8c/cd/dc9df6b57c043037920a3896ccb08a21e0595fe592b7acb9fc85acaf0698/chardet-7.6.0-cp310-cp310-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "4c3da294de1a681097848ab58bd3f2771a674f8039d2d87a5538b28856b815e9", - "url": "https://files.pythonhosted.org/packages/b1/10/31932775c94a86814f76b41c4a772b52abfb0e6125324f32c6da1196c297/chardet-7.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + "hash": "dde4080fb6bb8db96e8c44893771bcc0d235f4c22cdddb194a765a65e3a72ba7", + "url": "https://files.pythonhosted.org/packages/ad/be/1c57863e21c1d6cb6b0fcdde313f8a24039eca01cc90ef0f0986c0964969/chardet-7.6.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "dc50f28bad067393cce0af9091052c3b8df7a23115afd8ba7b2e0947f0cef1f8", - "url": "https://files.pythonhosted.org/packages/b9/99/f6a822ad1bde25a4c38dc3e770485e78e0893dfd871cd6e18ed3ea3a795e/chardet-7.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "93d9df6089ded42ed1fe9f57e272c0b74bd0464d45c0c7d50f09f26f31105c3c", + "url": "https://files.pythonhosted.org/packages/b1/51/cd61c567092a6cec796144510a68aff158ebfc1df82950a45bae65f28413/chardet-7.6.0.tar.gz" }, { "algorithm": "sha256", - "hash": "23163921dccf3103ce59540b0443c106d2c0a0ff2e0503e05196f5e6fdea453f", - "url": "https://files.pythonhosted.org/packages/c4/4b/1361a485a999d97cac4c895e615326f69a639532a52ef365a468bd09bad1/chardet-7.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "55a4c31adc7c7e83ad412f2f66b6b7358d0d4fe67505e7f58e18f68f75d341bb", + "url": "https://files.pythonhosted.org/packages/b9/66/acab13c5bbe55410273530a41b83fdcde0b653f6bded34702504c0ab95cb/chardet-7.6.0-cp310-cp310-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "9a4904dd5f071b7a7d7f50b4a67a86db3c902d243bf31708f1d5cde2f68239cb", - "url": "https://files.pythonhosted.org/packages/e5/59/a32a241d861cf180853a11c8e5a67641cb1b2af13c3a5ccce83ec07e2c9f/chardet-7.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + "hash": "4b81d3f7d7914442d5f7d515b8c6d79cee6b794bc208971fb6902f176671166a", + "url": "https://files.pythonhosted.org/packages/f2/34/0fd9d566df9647d0820a35b4d119db971da3ae9be20899d2b4af11d7ab58/chardet-7.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" } ], "project_name": "chardet", "requires_dists": [], "requires_python": ">=3.10", - "version": "7.4.3" + "version": "7.6.0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", - "url": "https://files.pythonhosted.org/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl" + "hash": "6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6", + "url": "https://files.pythonhosted.org/packages/cc/61/d01fc49b8dea277640b55a9e15960dbca9fdc8c9fde18e572d39c59f4019/charset_normalizer-3.5.1-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "e9fbdce1e47394b09bc9f26ab117dfc8d6491977a11d86f592bb42c779db2fda", + "url": "https://files.pythonhosted.org/packages/03/6d/439231dfc3ccfa6f8c06477b7da2219cbd41a2de3d49084df8ec7b5100f2/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "c7b742bf31c88566b4bb6335a7f393bb322e580b6bb98df7bd0c25e6e3519ce8", + "url": "https://files.pythonhosted.org/packages/0d/35/731ac04aa0a097fc1c97f0994c375bdb230c6c96619db794208fe664e9ce/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "4c4fb141a727957c93edfe5c32a26ceb6b5f6461d67146e2d39f51e16170bea8", + "url": "https://files.pythonhosted.org/packages/18/96/2b3a21492d9f65171ac75d872f5018260013d00bfa0ff70ec9f179148cbd/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "07ffd07412fc5d5e84cd8952acf9ff7e4ed7a708e69d1bada19d8ba91711353f", + "url": "https://files.pythonhosted.org/packages/20/c8/c36f6e0b2dfec351bd38cbc05362697e58bcd073d7dbd95154290c9714ce/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "0722590aabf9dc6a6c0343d523c05458fa2b5047dbe6302fd526bb570600753f", + "url": "https://files.pythonhosted.org/packages/28/f0/0c0ceec6d98b7daa62e361e418135d59685811d79ba11529aad5cdf15e84/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "36047af20e17097c3bb9476c2b7655f2f7aa51322c0ba58c07695bedf755a950", + "url": "https://files.pythonhosted.org/packages/2b/b8/11d4840bfc99330cc7fbcc2681ee5a044553a6e77655508d8f9b2bff7b34/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_armv7l.whl" + }, + { + "algorithm": "sha256", + "hash": "5b6d1386bf0096d26d3a863dc0a487a5b4eb9aa93cf5ba69683d29dde6b9d60f", + "url": "https://files.pythonhosted.org/packages/30/27/78873dc8b6a56357517b74b6bb9568b80450e7bb4f6ef7e3fa9d22aa0bd7/charset_normalizer-3.5.1-cp312-cp312-macosx_10_13_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "96eefc178f8636b9c760c5829345307fd81cfae9ab1e80997dbddeb0f54ee9a3", + "url": "https://files.pythonhosted.org/packages/32/cd/4f564b8f132de25db594efc706897069f016790cea63a5669c9df2675f64/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "7235dc28fc6dd9d832ac7c7bce95367dedb85929f17368a0c2bee1e080b9acbf", + "url": "https://files.pythonhosted.org/packages/35/5a/337e4663a5eae6de99db940ee8066d4145caafb61327db62deda15313cce/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "3588e376b3ea2eea84976f67273d679f229e24c66dce7b82ae45aef04ff6e072", + "url": "https://files.pythonhosted.org/packages/35/8a/3d130aeabcaf3d2466af76b7b141c08d9e89c9016ab4b7cdd0f7dc2d1c62/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "7c0c10730342b0c9b35dd1d619beb8214e520bd96a1f870f452680b238aab3e0", + "url": "https://files.pythonhosted.org/packages/38/fe/341861ac118dae06f3ec0eb487488af52128f2ef2faf0b11003944d22259/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "bd6c173f04743d483881bffa1478d5a4624475b8cd1d2194956a75548e191c18", + "url": "https://files.pythonhosted.org/packages/42/85/f9e22af69af67c54cce42be9455d9c81294f918b4ccc454db01f66efcac2/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "5d8531a6569d025f68e2321e7638fb7978f23db58e5f69f56913837aae03816e", + "url": "https://files.pythonhosted.org/packages/46/22/111e5be3b740d5c2a5bfcedb3d237b6591e5c2e82ae9d6ffcb121fe0909c/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_armv7l.whl" + }, + { + "algorithm": "sha256", + "hash": "343fb4f2821043bd87095f7b08a1a181febc8e36ac64212143bbfd0a0e1bc235", + "url": "https://files.pythonhosted.org/packages/49/e0/716601f3cc69be7b198951150c75ead1ece33c3c8036ff6ffa46029659a0/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl" + }, + { + "algorithm": "sha256", + "hash": "00668ebb0609751758682eb0b5857e7c35b9f00e84dfdef062e103244ec94d45", + "url": "https://files.pythonhosted.org/packages/55/53/7d819bd23a00ef45039146fa2cce1daa2f0771e758c5653ee1f6edac91ed/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl" + }, + { + "algorithm": "sha256", + "hash": "494b70049a4d69aec6e8137c13af4cf8db8c9f9820a1392ac293b0dd2987a818", + "url": "https://files.pythonhosted.org/packages/58/ad/b9aecf38d805cbcf84fa94f14c5d972a16561e20296a11dc799a5dcf3763/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "41876ee62a3dddf48ff1121ad8f0798032aa03f2fd35f21f34a4cab14f18d8d2", + "url": "https://files.pythonhosted.org/packages/5b/97/fb4e82231aba271ffd775a1b4993b0defc4e3059f286ae41d9433409fe85/charset_normalizer-3.5.1-cp37-abi3-macosx_10_9_universal2.whl" }, { "algorithm": "sha256", - "hash": "5e226f6218febc71f6c1fc2fafb91c226f75bdc1d8fb12d66823716e891608fd", - "url": "https://files.pythonhosted.org/packages/01/c4/4fa4c8b3097a11f3c5f09a35b72ed6855fb1d332469504962ab7bafcc702/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + "hash": "ce854f5f478050ade5a238731c4ca985a7d3b3cb53ff600a9b5c3b689b5f0a7a", + "url": "https://files.pythonhosted.org/packages/69/2b/d8be3523ddf9f0b0f3e56d1359034aa10653a4d11564c697f802b4775766/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" }, { "algorithm": "sha256", - "hash": "0e94703ec9684807f20cfb5eed95c70f67f2a8f21ad620146d7b5a13677b93e5", - "url": "https://files.pythonhosted.org/packages/0b/e3/85ec501f206fb049259288c1f3506e53876937fb00edb47009348e66756b/charset_normalizer-3.4.9-cp311-cp311-macosx_10_9_universal2.whl" + "hash": "eda059b6bc8bc0812d626fd91a7ce01bf583df0a61296eff390fd94141a34e30", + "url": "https://files.pythonhosted.org/packages/6a/b6/034f6802e9c3f6418966cfabb7db8c9252cc2429c5098f41cc43af804149/charset_normalizer-3.5.1-cp311-cp311-macosx_10_9_universal2.whl" }, { "algorithm": "sha256", - "hash": "871ff67ea1aad4dfd91736464934d56b32dac49f9fbe16cddba36198a7b3a0db", - "url": "https://files.pythonhosted.org/packages/0c/74/2f62c8821b969ea3bd67cc2e6976834f48ca5d12664d2559ebcd9bcfbed7/charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + "hash": "b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08", + "url": "https://files.pythonhosted.org/packages/6f/89/bb5108dc6c3651dca963f2b0a3ba19bbcb370c94e1b6d3e0e844a58e6dca/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "65a7ff3f705e57d392f7261b6d0550fe137c3019477431f1c355e0db0a7d3e15", - "url": "https://files.pythonhosted.org/packages/0c/e7/aaf6da33fc9f4691cda8f7efbc9f69179d3d39ec8a4799baf273ee1d8db0/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_armv7l.whl" + "hash": "d1ee1e296209fdce05b81b663250eefa02213a2da7b41bf26f7829b8ba3545aa", + "url": "https://files.pythonhosted.org/packages/71/aa/554e2614f38fc34c58ff1d0911ae8535ad2516440d5482d76fe59f1088b0/charset_normalizer-3.5.1-cp310-cp310-macosx_10_9_universal2.whl" }, { "algorithm": "sha256", - "hash": "e9701d0049d92c16703a42771b98d560b95248949f23f8cf7b4eddd201814fb9", - "url": "https://files.pythonhosted.org/packages/1d/85/181c652953eb5276d198f375b1dd641047392050098100a3a02d6534f657/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_aarch64.whl" + "hash": "a2028475ba855475b8b4d3cfeb4994269c967aea8b9892dfba907f4263a863a3", + "url": "https://files.pythonhosted.org/packages/75/9c/019fbb9f4834491a160951349b1a3714439376f66e5f7cf18b4f18f0c7aa/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "60f44ade2cf573dad7a277e6f8ca9a51a21dda572b13bd7d8539bb3cd5dbedde", - "url": "https://files.pythonhosted.org/packages/33/9a/895095b83e7907abd6d3d99aad3a38ad0d9686cc186cb0c94c24320fe63e/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_aarch64.whl" + "hash": "1d1c7a53a6c2103925cdd6d7229f8c567379f211c869793df679f2e9f738c369", + "url": "https://files.pythonhosted.org/packages/76/84/6f1290fa07ae6978d3960caa3eb1b8019bf9284ab7c2297b00c099ef4250/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl" }, { "algorithm": "sha256", - "hash": "bcf74c1df76758a395bf0af608c04c82257523f55c9868b334f06270d0f2112b", - "url": "https://files.pythonhosted.org/packages/36/31/a276bb2e66243072a3fd06fdcab9cbb61a305b02143d70d2bda21d888fa8/charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_armv7l.whl" + "hash": "e199fb99720074809a7720f1c0b4d919eea8b87e88713e0f8f602f7bef543d9d", + "url": "https://files.pythonhosted.org/packages/80/c2/a7379b840292d0c1ab9fbd17d1f3967aa81794dc95bc74be8999d7fedcf7/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c0323c9daef75ef2e5083624b4585018a0c9d5e3b40f607eed81a311270b934b", - "url": "https://files.pythonhosted.org/packages/44/95/80282cce0fae9c3061203d723ee87da996aed79679e65d8935050ee7ca1f/charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_armv7l.whl" + "hash": "b54e7e13267d49ffbfe68e25b3cbd774dab38fa37238f71265e91b36146eb21c", + "url": "https://files.pythonhosted.org/packages/82/38/083a24028304bc85bb9e376fed801178423dcbb67495f73b6ea0624e1894/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" }, { "algorithm": "sha256", - "hash": "9104ed0bd76a429d46f9ec0dbc9b08ad1d2dcdf2b00a5a0daa1c145329b35b44", - "url": "https://files.pythonhosted.org/packages/4b/4c/5361f9aa7f2cb58d94f2ab831b3d493f69efb1d239654b4744e3c09527cb/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + "hash": "950f23cb393f85543777b0433f082cddd25b51ab398eac7971146495679efe5f", + "url": "https://files.pythonhosted.org/packages/87/bd/fbc24d825c66f1c74f6ccdea3742c3d8354a4888e86d1315a197fee69061/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "4773092f8019072343a7447203308b176e10199920eb02d6195e81bbb3274c29", - "url": "https://files.pythonhosted.org/packages/4e/6e/de0229a7ef40f6f9d28a837eebf4ec47bdca5dab4e900c84f22919af636a/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + "hash": "4582c27e8c889d64811987b5967fbd3ae0c823fe1fd933b543d55ac20bb475fa", + "url": "https://files.pythonhosted.org/packages/9a/4c/be49ada26b1f0232d57aa89bbebf997a5cc2332a5616b6eca26ff680044d/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "7b86a2b16095d250c6f58b3d9b2eee6f4147754344f3dab0922f7c9bf7d226c9", - "url": "https://files.pythonhosted.org/packages/50/78/ce342ca4ff30b2eb49fe6d9578df85974f90c67d294113e94efdd9664cbd/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + "hash": "a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99", + "url": "https://files.pythonhosted.org/packages/9f/2f/fe3f187327aac18e2d54e9d2b08e15d27bf9b642d9e51c219f130fc34d1a/charset_normalizer-3.5.1-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" }, { "algorithm": "sha256", - "hash": "b5314963fce9b0b12743891de876e724997864ee22aa496f903f426c7e2fa5b2", - "url": "https://files.pythonhosted.org/packages/5e/be/7ee4453d7e88dfbc4104ccd34900b9f2c7c17dac22881865fe0e82424a25/charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + "hash": "0b2b1b3fa5670c127b246df1d0c059defd41f689a868a3b9d79df9b1cac42d22", + "url": "https://files.pythonhosted.org/packages/a4/c8/ab42b07cfd82e919f427fcfaa7c41abae8242833ad1aad66d42bae40b669/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl" }, { "algorithm": "sha256", - "hash": "79580094b00d1789d1f93ea55bc43cb2f611910c72235b7657f3482ddcc1b22d", - "url": "https://files.pythonhosted.org/packages/63/01/f2fb3bd3a73be48b173ee0c6aa8d2497af97d5663a8c4c4b491de4c62f7a/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_x86_64.whl" + "hash": "f9f8405c2c758532c74fed975dbee57be1f31a6e865c031870c79a6ed3212ada", + "url": "https://files.pythonhosted.org/packages/b1/ba/ef83ae3aca816393decfa3530976f38a79812d707b80b580ac33b83f9877/charset_normalizer-3.5.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "45b0cc4e3556cd875e09102988d1ab8356c998b596c9fced84547c8138b487a0", - "url": "https://files.pythonhosted.org/packages/70/4a/ecbd131485c07fcdfad54e28946d513e3da22ef3b4bd854dcafae54ec739/charset_normalizer-3.4.9-cp312-cp312-macosx_10_13_universal2.whl" + "hash": "ba2f37ee79e6338845261a3c5b1784e5d1acdff2c0785b284f1b633033d136ab", + "url": "https://files.pythonhosted.org/packages/b2/2c/45847198c16f4b38090cc7423b2b6a9008e438704d8ab413211832498d31/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "16d10d789dd9bcca1173c95af82c58433122564b7bc39385124be735a35cbe99", - "url": "https://files.pythonhosted.org/packages/7f/f4/ffbb83546e1f198ecc70ecd372b65cf2b50f9068b380abd67640f17a8e18/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + "hash": "ba501e667c17d8411f98e67a022d9604ef179aff0e459b7e292c796837c13573", + "url": "https://files.pythonhosted.org/packages/b7/52/643d11ffd60e9ac2fd1fb87e167a19285b9eefeff4a40e63c87cbfbeab36/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c1225416b463483160e4af85d5fc3a9690ccb53fd4b1865a6437825f5ede3209", - "url": "https://files.pythonhosted.org/packages/80/33/6c99c1b3e6b8bf730e1bc809b9a2608f224145069114c479a2e9e1494346/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + "hash": "c1dcc36dcb96abc02236e182d17e0f71430152a6c2c7447421da2d2dc144edea", + "url": "https://files.pythonhosted.org/packages/b9/2d/918d0e98a0e679469ed05bb2d90c2088b4d315bb612969d8499f76fb5210/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_armv7l.whl" }, { "algorithm": "sha256", - "hash": "03d07803992c6c7bbc976327f34b18b6160327fc81cb82c9d504720ac0be3b62", - "url": "https://files.pythonhosted.org/packages/83/dc/9b29fa4412b318bf3bfea985c35d67eb55e04b59a7c3f2237168b0e0be6f/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_x86_64.whl" + "hash": "a545775cfe815855ea32d7c27731d79da358ef2055b4a25830231b1622dd18aa", + "url": "https://files.pythonhosted.org/packages/b9/90/082cc45599c392f28c036a497f49e0634041a785fc3849c80ccf396d096f/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_s390x.whl" }, { "algorithm": "sha256", - "hash": "90c44bc373b7687f6948b693cceaea1348ae0975d7474746559494468e3c1d84", - "url": "https://files.pythonhosted.org/packages/87/3a/ad914516df7e358a81aae018caa5e0470ba827fa6d763b1d2e87d920a5f6/charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_armv7l.whl" + "hash": "6e2912d4babbc65196ac13c2f53468dc57fb8b9c25ef913e8c59ddf7c6dc0e1b", + "url": "https://files.pythonhosted.org/packages/ba/ed/1dd7cfebb4e75812934c49ca3b79757d11948053f7937ab7070c151f3c55/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_s390x.whl" }, { "algorithm": "sha256", - "hash": "a1786910334ed46ab1dd73222f2cd1e05c2c3bb39f6dddb4f8b36fc382058a39", - "url": "https://files.pythonhosted.org/packages/a1/34/ef5c05f412f42520d7709b7d3784d19640839eb7366ded1755511585429f/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_armv7l.whl" + "hash": "3d27167433c0d5f18dc850f07d0b3816221984fecdc405d6c157a6f0b8f8e9e6", + "url": "https://files.pythonhosted.org/packages/bf/eb/239c84503cc9e3ba6eb34686a24bc66e84f3924efdd7e38e751a19f6bc10/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "04ce310cb89c15df659582aee80a0603788732a5e017d5bd5c81158106ce249c", - "url": "https://files.pythonhosted.org/packages/a5/34/49b9060e8418b14fb5cba9cf6bfb383111e2538a03a1fb18e66a95aeb3d5/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + "hash": "4b599739b93b2cbeded49645ae3c8d1405c29ddfbceac1545c87a3f9580a9e96", + "url": "https://files.pythonhosted.org/packages/c3/92/de7e32ed05341e7a9c4c877c318418197b7f2d66a3b68d561bf2ac57ca3e/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" }, { "algorithm": "sha256", - "hash": "3d92613ec25e43b05f042302531ec0f00b8445190e43325880cbd6ab7c2581da", - "url": "https://files.pythonhosted.org/packages/a5/ff/c946d63bc3786d5b84d960b0f7ab7e25b828486a946b5aa997625bcaf6a6/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_armv7l.whl" + "hash": "f5542f9b941279d82d41eb0aa9f98eba36fe4df5c7086c651df7944935b37182", + "url": "https://files.pythonhosted.org/packages/ca/7b/311b3e02e8c4092400c449c850a760d8c45d900983c83a70cc07208c551d/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_riscv64.whl" }, { "algorithm": "sha256", - "hash": "cd6280cf040f233bd7d3407b743b4b4c74f70e8e1c4199cb112a62c941c0772a", - "url": "https://files.pythonhosted.org/packages/ad/81/8e983840c6e5b93b33c2ba81aa3d52c2e42f0e9a690ce7607a2e61da4a5c/charset_normalizer-3.4.9-cp310-cp310-macosx_10_9_universal2.whl" + "hash": "4abdc5f9ad448c1ecbfae2974b820535d6bc6e7eef63babbab3d81cf46968c71", + "url": "https://files.pythonhosted.org/packages/ca/85/f82f8a92e31c7519410e2e1afdc630f28ec47490ce2c09a11c1a43cbb459/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_s390x.whl" }, { "algorithm": "sha256", - "hash": "280081916dc341820640489a66e4696049401ef1cf6dd672f672e70ad915aca3", - "url": "https://files.pythonhosted.org/packages/af/ba/5e5007c370702f85d2ef75791fac7943ed41e080364a673b20142e430e3e/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_x86_64.whl" + "hash": "ae4a097991662cd4fff0ddc74e0fe7874f82e00042fa0ea00855645ed0c79598", + "url": "https://files.pythonhosted.org/packages/d3/05/71bfc5caa0abcc45aea1f6a4d50ac68e59605ddc7666fe8494f4cd229665/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "304b13570067b2547562e308af560b3963857b1fa90bd6afd978130130fe2d6a", - "url": "https://files.pythonhosted.org/packages/b3/46/03ddc7da576d814fe0a36dd1f0fd3258e95404b4b2e3c026b7923d7e133f/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + "hash": "aa2bb0b37202dca27175591f761108b5d34096ade1191ffe4808bdf6b1571488", + "url": "https://files.pythonhosted.org/packages/d5/fa/6a7e2a7c4b5451912b8c417732df79574354443592a88d616de03da66ae5/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b", - "url": "https://files.pythonhosted.org/packages/bd/2a/23f34ec9d04624958e137efdc394888716353190e75f25dd22c7a2c7a8aa/charset_normalizer-3.4.9.tar.gz" + "hash": "2f293479cce755c75f1697e87c409b7ae4c555c7dfecb6e988ad13abba943031", + "url": "https://files.pythonhosted.org/packages/d6/aa/a69a2028e8bd052476c245460ab19d7de595de084dd968f2d75cd50c3e25/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_riscv64.whl" }, { "algorithm": "sha256", - "hash": "2a441ea71902098ffe78c5abe6c494f44160b4af614ed16c3d9a3b1d17fd8ee2", - "url": "https://files.pythonhosted.org/packages/c3/69/2a5385192e67175f7d8bd5ce4f57c24bc956439adeae5c13a99aa28a53d1/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "cee5dd7c6fb5dd52a0fe2a740f9bc6e3593f5f8b1788bde49de02086f30182b2", + "url": "https://files.pythonhosted.org/packages/d7/c7/9e48cee5c161fe24da823b61bf381921d77cb994a0a4de148e95018c1984/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "9cdef90ae47919cae358d8ab15797a800ed41da7aba5d72419fb510729e2ed4b", - "url": "https://files.pythonhosted.org/packages/d7/74/3c12f9755717dfe5c5c87da63f35d765fa0c00382ec26bf23f7fae34f2ba/charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + "hash": "6117b84ea48435e5356dc737f5121485c30920ba43375fa7b434fd753df0eac3", + "url": "https://files.pythonhosted.org/packages/e5/3f/143b048436775b0f76ac3eec145c019e8173ccc2885c8f20319b996d5e83/charset_normalizer-3.5.1.tar.gz" }, { "algorithm": "sha256", - "hash": "67830fc78e67501f47bb950471b2dcb9b35b140084429318e862895a8e89c993", - "url": "https://files.pythonhosted.org/packages/d9/8d/feabb82cb49fcad14515b1d7d1ca4787b0da7fc723a212bf89bc9e0fac52/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_aarch64.whl" + "hash": "6e5e4d73d588ca5ed09df1b7dcd1b203d1df3c542e3f50d126c947d432b10731", + "url": "https://files.pythonhosted.org/packages/e7/80/b9348b5d3041209f98b4cdad7655766369233f1d533f4f4f7558e9717bec/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "aa99adc8f081b475a12843953db36831eaf83ec33eb46a90629ca6a5de45a616", - "url": "https://files.pythonhosted.org/packages/de/d1/b4319dc3229d8272fba305e206fc0a148e2de8d4087917ce62ae6382f359/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "e6621fb2a4988d6e53eedc455e5903e2679f3967b8acb3d639f1b63c14a2e893", + "url": "https://files.pythonhosted.org/packages/e7/a0/47b18adeed31c8f16ba9700f32c1b18594cfa09f47eb672a488c273c22bf/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "9bb41182d93ea91f60b4bc8fbf4c820c69ef8a12ab2d917f3f1834f1acad07e8", - "url": "https://files.pythonhosted.org/packages/e8/5f/b98b8da398637b551e427e7be922bdec19177dc54d6811dcdaa503f23aac/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + "hash": "aa1099b956fb795e686d073568f6dc002a0bb89765ea6d5b055dd7d9bf1b116c", + "url": "https://files.pythonhosted.org/packages/f0/3e/48f4cd187b1c33189d86039e9cbe4f92c05454175504b44ff81806d4d1bf/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_armv7l.whl" }, { "algorithm": "sha256", - "hash": "9b2aff1c7b3884512b9512c3eaadd9bab39fb45042ffaaa1dd08ff2b9f8109d9", - "url": "https://files.pythonhosted.org/packages/ec/96/5d9364e3342d69f3a045e1777bc47c85c383e6e9466d561b33fdb419d1f9/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "6ba32c4d2abf1d2fe7cf27d280f4cca5664233b0f885549c7761719eb977f486", + "url": "https://files.pythonhosted.org/packages/f5/28/c2028e7021fb89c6e56868ed0e387b8e9aa811abdd2ab3208d6578d2c930/charset_normalizer-3.5.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "b39b69b347e5e47a3b5b8cfc005c68c1ba347474e3960236c4944a8ecd174962", + "url": "https://files.pythonhosted.org/packages/f5/7b/ade0a122600319dfa0b1000ab0f9731c94a817904cf3c5de408c73a4ede7/charset_normalizer-3.5.1-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "366ec70f5547c640d3ce1985722490f23faf4eb5216a7eeba78277490e78dacb", + "url": "https://files.pythonhosted.org/packages/f5/e3/38b975422534a608f98c360e79c2f07c763d66dd4272300d45fb1fee54b0/charset_normalizer-3.5.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + }, + { + "algorithm": "sha256", + "hash": "96fef3e886d6a9874b14f27fc193fbdc69d5d8035783d86aa4e1cea594e695f9", + "url": "https://files.pythonhosted.org/packages/f6/0b/c5292a2462d69b7378ea89793bbb5b2b6fcf6f7dd6d1667f9619094ad553/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "aae2ee51122d3ae968a3837d97dc24a0aeebb0dea23694422cd172bd30017cd6", + "url": "https://files.pythonhosted.org/packages/f9/d2/d2aad6fe0dbb44b194bf3becb60f5a0ac48446ade999a47fe7bb41eb09a7/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "f298e218441525d3794428b4c8b8fb8662c6d3ea79925d4807ee6b9a96a3bca5", + "url": "https://files.pythonhosted.org/packages/fd/4c/9044135f42127630b6fa742feb51256353f6ab87a78f2fdd1de3de955a7f/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_riscv64.whl" } ], "project_name": "charset-normalizer", "requires_dists": [], "requires_python": ">=3.7", - "version": "3.4.9" + "version": "3.5.1" }, { "artifacts": [ @@ -1069,21 +1177,19 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", - "url": "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl" + "hash": "255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", + "url": "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", - "url": "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz" + "hash": "ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", + "url": "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz" } ], "project_name": "click", - "requires_dists": [ - "colorama; platform_system == \"Windows\"" - ], + "requires_dists": [], "requires_python": ">=3.10", - "version": "8.4.2" + "version": "8.5.0" }, { "artifacts": [ @@ -1249,153 +1355,153 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "084ef1af862eb07ec46d25f68689f2102a9fc0e05ce7b80f14f5fe51e4eef0f6", - "url": "https://files.pythonhosted.org/packages/ab/f8/614dc7e051418cfe53d55173c1e24c6b0085e89996fe90508c2fdf769aef/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl" + "hash": "804728ce710890870f3aaa344b2e161172d258d768ac139d02cfd9092d0d94e6", + "url": "https://files.pythonhosted.org/packages/51/cf/5b3f53a0b74d122f023476ede40ba5d3e70d5cf475f73b899740d26a4fb2/cryptography-50.0.1-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl" }, { "algorithm": "sha256", - "hash": "36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db", - "url": "https://files.pythonhosted.org/packages/09/41/3797cfaf69cae04a13ee78ebd83f0678d9c02b4779d21ce24445326f1a69/cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" + "hash": "2b34d76a652ea2b6faf777c35df230c5637842cd904e04f16230c3f9f03e4361", + "url": "https://files.pythonhosted.org/packages/0d/5c/13ea642e08e2544d0f5396122055f4820cfacb3203562197b5967125ea97/cryptography-50.0.1-cp39-abi3-manylinux_2_31_armv7l.whl" }, { "algorithm": "sha256", - "hash": "ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a", - "url": "https://files.pythonhosted.org/packages/19/2a/5bb823f5bedcf80718cea7fbc95ec5515cca3769633c4b01a32be7f30e7c/cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl" + "hash": "be224a65493ec5b74a158ff22a5522ce4a5ca1e543c647a3a4730d4a09e5f959", + "url": "https://files.pythonhosted.org/packages/13/0e/b1f92e013228111413f2e6743948b80bc24dfd3c1b87ba98ceea16f5df89/cryptography-50.0.1-cp311-abi3-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493", - "url": "https://files.pythonhosted.org/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz" + "hash": "5fe939deeb161024a6be98229c953b6591fef1f41214497a78fe793a244c017f", + "url": "https://files.pythonhosted.org/packages/14/9a/6d3a4d7852e22d657438b7bf51f66102c7d71c0e1fafeec652281d0403e5/cryptography-50.0.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6", - "url": "https://files.pythonhosted.org/packages/20/2c/0622f20ff02b2ef32558733443805dc82fd4c275be01b2d19d14676f3a1b/cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl" + "hash": "d63ae8f6481fec907ac0f588eee8a90aefde112c633131fe540e5711ddbb5a4e", + "url": "https://files.pythonhosted.org/packages/1d/e0/e786934472e3ac4ecdecc7b129a0ca1a2a40dffdafcf2c3ea9d4397f8def/cryptography-50.0.1-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl" }, { "algorithm": "sha256", - "hash": "9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64", - "url": "https://files.pythonhosted.org/packages/24/01/186c825898477d77e2324d5360fefe622ff1d8d1963ec0554e2cada8ec77/cryptography-49.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl" + "hash": "2a93d05e34d5f67fba6f891fe85d929999baa7195e853923ea6d7576c9e68c5e", + "url": "https://files.pythonhosted.org/packages/1f/ab/89e2b798d2c3925f82e2bb72d5979f3d2f6da2dd22ef4a8cd8b70d920039/cryptography-50.0.1-cp39-abi3-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18", - "url": "https://files.pythonhosted.org/packages/2c/99/2d13299eb3dd27b02dcfaafcc91d6b5cb3329f7cbd6d8f51921acd566c1a/cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" + "hash": "4c4188f7c0cf655be5c06342b817ed0f9595b69ffa2b12026e5353eed29dea88", + "url": "https://files.pythonhosted.org/packages/27/3a/3c5f80daa4dcd47323c7af8a2fcb90de27a33564d4fcac69846c0972691a/cryptography-50.0.1-cp39-abi3-manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4", - "url": "https://files.pythonhosted.org/packages/3d/df/40577043ca124e17012f408ddddaeb213b856336ac82ddb3bc915f39e29f/cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" + "hash": "359e62deae718bce96170e223fdcb6357e4fbd3bb7a3a75f4430763532560e49", + "url": "https://files.pythonhosted.org/packages/29/ba/042ca458b8c64348c768284b5d23e69b92ed53d057ab779fee628564676d/cryptography-50.0.1-cp311-abi3-manylinux_2_31_armv7l.whl" }, { "algorithm": "sha256", - "hash": "53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2", - "url": "https://files.pythonhosted.org/packages/4a/91/01ce7303a4579e6d3a6abef01bd322848e9ea7a219adcabc5048b9033571/cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl" + "hash": "e2ca8fd1b6b4b82a1c4cb02841d0837e3c12336c2e24b520ab8ab3b969733d8f", + "url": "https://files.pythonhosted.org/packages/39/3b/e96c1ef71edef71057c7e3c3d982ce8fda554e0c52d0cc19c18845cde3eb/cryptography-50.0.1-cp311-abi3-manylinux_2_34_aarch64.whl" }, { "algorithm": "sha256", - "hash": "b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f", - "url": "https://files.pythonhosted.org/packages/4f/01/339573cf1023163a400b0b5d16f6d507de413b9f60be6fd1b77feeaf6737/cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl" + "hash": "5fe002589592ed749ce77fe0695fcbd3500dd61d7d6db5858a7544c612fa8e45", + "url": "https://files.pythonhosted.org/packages/55/32/38c0d344b98c06d34b5df8946565a9c0d6dbf32c8e0730a7f05f0a3c6cab/cryptography-50.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b", - "url": "https://files.pythonhosted.org/packages/62/99/a2c95cf8293f07491e9e27c20cc4dcd18176d944e674679adeb1d0173fd6/cryptography-49.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl" + "hash": "ff838d62ec1bfce4f9ba7fa16f4a7b554cd8d0c299e6be37502161a660c84eef", + "url": "https://files.pythonhosted.org/packages/57/26/e6d4fc8512a51a5f9ee7bfdbfb853bce1197087df40c9ad993ad370b846f/cryptography-50.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" }, { "algorithm": "sha256", - "hash": "fc1e275c2f1d97b1a6450b8b0ea3ebfa6e087a611c2b26cb2404d48588abab7b", - "url": "https://files.pythonhosted.org/packages/63/d3/4a83af35d65e3fad632c926fad684c193ea4398569ccb0bbbc7fe8f5dc9a/cryptography-49.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl" + "hash": "05ba322c4da95b262a212c345af888ef2c37c88c0509756ea00a0e6d68850f23", + "url": "https://files.pythonhosted.org/packages/5e/a5/9ec7e81e8526c0d7a387d73386b2daed3f39e10d81a85930bd1b6bfba65c/cryptography-50.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" }, { "algorithm": "sha256", - "hash": "6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8", - "url": "https://files.pythonhosted.org/packages/67/d0/a5fcd3515f0bae49a7b6d0413cc1bdccdcc1fc0047037a0d480642cdc5d6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl" + "hash": "fd3718b960d0b5dd213cdf03f3bcb7000e69dda0de8b956061947ff6bcff5558", + "url": "https://files.pythonhosted.org/packages/63/8e/f1f955e0921dd2b6d22eae7e8d24a4c4b638d10735ffbf6a71f99eb0fcb8/cryptography-50.0.1-cp39-abi3-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9", - "url": "https://files.pythonhosted.org/packages/6c/72/3e798c064bc39e471008075d0f9bc9daf77a80879c092e4a8e170c585ed4/cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl" + "hash": "2ebbfb0f1fed745e91796e3e1080a1440423fdae8ece1b995a1d80883a409054", + "url": "https://files.pythonhosted.org/packages/6e/2b/214cf0cf93db9628c3c20c896b229f327f6fb1b20e4b3743d8ad3f00af8b/cryptography-50.0.1-cp39-abi3-manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e", - "url": "https://files.pythonhosted.org/packages/6c/a0/db537264e234f7273a73ec020873d6d6b39dfd8a53db78b550ca8320440e/cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl" + "hash": "fb4b9672d389c738b175c4166e78310f8a70358886aacd9173ee03a85ffdc671", + "url": "https://files.pythonhosted.org/packages/73/35/5c3717edf9e68a0550ce04e28eab493fe545eccd81742af03f6a75fe260b/cryptography-50.0.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7", - "url": "https://files.pythonhosted.org/packages/6d/88/05563c7fe2e914e87d1a536d06fe83e66b4e1d95cb593e05aea375531da8/cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl" + "hash": "9ebcdd5519be9b652a46f507817a74591774fc3d6923ac364e4dfa64e36b291b", + "url": "https://files.pythonhosted.org/packages/7e/22/c3654cccc856e9d682817b04ac3ee79731cb09ca6f95996a95c904de2883/cryptography-50.0.1-cp311-abi3-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459", - "url": "https://files.pythonhosted.org/packages/71/fd/577302e213a1be9468f92d1afef66fcf1ef83d516819d9992ca547f592bd/cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl" + "hash": "e22dfed744bd4002e909464cb23d2f0b05c6f3113a79ef2e9864a53db737c733", + "url": "https://files.pythonhosted.org/packages/7e/3c/0e77bd5ffcf078e9dd27d3074aad6c030d9b10d0bf69329d573c927a188c/cryptography-50.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" }, { "algorithm": "sha256", - "hash": "42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b", - "url": "https://files.pythonhosted.org/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl" + "hash": "ca83d00d9e69cd5eb63f2e69c3a5a59e0cecae5ae14c6ae0b35830fe3b37bad0", + "url": "https://files.pythonhosted.org/packages/84/a9/ee16a903f13755e914d1eecc482fe64d1f10761c3960e5d8fa6837377aff/cryptography-50.0.1-cp39-abi3-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "b47db11c2c3525083296069b98ac5221907455e989ae0c2e3008bde851921615", - "url": "https://files.pythonhosted.org/packages/94/64/2923570ac1c0bd3a737aa366ac3abbbbde273042308b8cde95e2364a6e6a/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl" + "hash": "01f41478cf33fc605a6a089cd56d28b45c6c0b45a1928b61797f2621a04bac71", + "url": "https://files.pythonhosted.org/packages/84/d5/7d1fe1cb93f91c428093ff234e128c89ba8ea61a6f26aab406081f9b996e/cryptography-50.0.1-cp39-abi3-manylinux_2_34_aarch64.whl" }, { "algorithm": "sha256", - "hash": "966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db", - "url": "https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl" + "hash": "51afcfceb15597cf2635068e4ac9a56b2abde622edde17f37d85fd7b5306497a", + "url": "https://files.pythonhosted.org/packages/85/66/6ccca4722987ddedaa7fc9c3f4708af7431f5535666c174350830888c6b7/cryptography-50.0.1-cp311-abi3-manylinux_2_34_x86_64.whl" }, { "algorithm": "sha256", - "hash": "2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36", - "url": "https://files.pythonhosted.org/packages/a0/84/84fe36f19caf857d61cb7fc9c63035a47ffabd84ea12d1d393148efa3615/cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl" + "hash": "9dde0a357190eb3b1da1bb9ab750e9c85cba82ca5977aa0836cbb94e92611239", + "url": "https://files.pythonhosted.org/packages/8c/eb/5d7124083e8d8cda8f5b348f544b71ad6f707ad63193758ef4d8e569da02/cryptography-50.0.1-cp39-abi3-manylinux_2_34_x86_64.whl" }, { "algorithm": "sha256", - "hash": "0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c", - "url": "https://files.pythonhosted.org/packages/a3/5b/c5246635d5fd3b64e0d45ae10e99fd32fe9676a79915ccfe5a61ba9af1a5/cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl" + "hash": "53e279950892dc102c6b4e52af03ae5ea92fac572a1ddab78ca73a997f62b69f", + "url": "https://files.pythonhosted.org/packages/90/34/9ce9a62ed9dc82ca9fd6a34445b6904af56e5f38b3eae2ed32e49c36053d/cryptography-50.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" }, { "algorithm": "sha256", - "hash": "0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69", - "url": "https://files.pythonhosted.org/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl" + "hash": "b8f852c65863251b9e3a1b8c150ce21e59b522dbb6a7d4bc80e680d38388e986", + "url": "https://files.pythonhosted.org/packages/ba/19/797e2aaac9df6a66f1550f49979dc1b1e39ecd2077501c30efa81e8d5d67/cryptography-50.0.1-cp311-abi3-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9", - "url": "https://files.pythonhosted.org/packages/a9/3c/f3ad17eecc1a57b0ba236dc01f90e783c51f4a2f35f64777cc4f47a184b2/cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl" + "hash": "5dd9bda1c12b4162f6ff568eeb5e0ff956c28d14406e875cfe8a63a2d414ff20", + "url": "https://files.pythonhosted.org/packages/bb/ad/5d6702db60b1e40b41ef513b6967ff5848f307d50f8449baf1634f5908f1/cryptography-50.0.1.tar.gz" }, { "algorithm": "sha256", - "hash": "6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21", - "url": "https://files.pythonhosted.org/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl" + "hash": "9cb3cb952cf5a8abd50c782a98a89d71699715e802fe349704b47f2425b42a94", + "url": "https://files.pythonhosted.org/packages/c7/27/8d207af749c453ee17ea087340b3f2b4adef75aadd1d277b1b129bdda84e/cryptography-50.0.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68", - "url": "https://files.pythonhosted.org/packages/c4/b6/d7696e4e890d6ae1469935164c9e5215c557671cb78d6e3f458ccceaa632/cryptography-49.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl" + "hash": "407fe2b6db00939c05c0e945e9914238f2f0a430974839429dafc82b1ee6bee5", + "url": "https://files.pythonhosted.org/packages/d6/51/3f9701867a46b6c1740c9b52fc4d3bed6cbdcfedcc9b6e64305c07f39cff/cryptography-50.0.1-cp39-abi3-manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c83782480a4a9da4d0feb51950131ba32e12e70813848b3343f6e18c28a66838", - "url": "https://files.pythonhosted.org/packages/d6/a7/f9dac0ab7f80368c56993a7bf638ef9935f825c91902798481fac0898138/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl" + "hash": "fc3ed7ebd2a8c96f5b166de0ab9b624996bef3b07bbeb19364dfb78222c22c80", + "url": "https://files.pythonhosted.org/packages/dd/04/557fc5ead96a829e0bc812a3b9dc4a52a2f27e4f7f5950da7ff27653a805/cryptography-50.0.1-cp39-abi3-manylinux_2_34_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "b39efa323140595abd3ecca8529d321ae50f55f3aa3ba9cc81ea56a6011953d5", - "url": "https://files.pythonhosted.org/packages/d7/70/2ba3769dd0ae167e2f33dfa9592d45db6ff9a61d62ca1a5b3d1bdd09068f/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl" + "hash": "51593d180cf6d179bde5c5d065bed81386b1f381656ae7d042b7ffc87a9895ad", + "url": "https://files.pythonhosted.org/packages/e1/1b/82f0f0d8858d4432be1af790477edf62aef90324041aa07c57e57bef1af7/cryptography-50.0.1-cp311-abi3-manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325", - "url": "https://files.pythonhosted.org/packages/e6/8b/43011f7ebe515a8aa20d61f290a326cd890c2e738e16e59eaff8d9c3a412/cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl" + "hash": "76de83fbd91ac49c0feaaa983d0748fd7a53176afac5fb3bf7478d244f0eb527", + "url": "https://files.pythonhosted.org/packages/e3/38/45abd72ef63f2e7d0754a6cacf97bd8b69512ace7f6130d24c39ece65da2/cryptography-50.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc", - "url": "https://files.pythonhosted.org/packages/f0/ee/6fca21d1ac73e06f8bef71940abfd4d2f6472b4bca284d770f32bd4086f6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl" + "hash": "e74591e283fe6eb956416c929eb58262a719fe0311fd9054c62c3350ed8760d8", + "url": "https://files.pythonhosted.org/packages/e6/de/d3cdc2815697aae84126cbd6a030ca7b6b452e28a88b501b836bd3aa7a86/cryptography-50.0.1-cp311-abi3-manylinux_2_28_aarch64.whl" } ], "project_name": "cryptography", @@ -1405,7 +1511,7 @@ "typing-extensions>=4.13.2; python_full_version < \"3.11\"" ], "requires_python": "!=3.9.0,!=3.9.1,>=3.9", - "version": "49.0.0" + "version": "50.0.1" }, { "artifacts": [ @@ -1528,13 +1634,13 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "6f7bb5c2309d1c4527bf15fc2a5da0b829e68e495430b890993b47dfea258ae5", - "url": "https://files.pythonhosted.org/packages/a8/55/92d859c16a37a9b70e2c682747750184b389a6b4d25321a4a1ec48d94b33/eventlet-0.41.1-py3-none-any.whl" + "hash": "6cae50e67fe6ae8bb7013e7fd4d8e0d0d20aeb9b3259b93f023c93eb6749631f", + "url": "https://files.pythonhosted.org/packages/23/75/9681fa59c4b27d9e34e8be682d2fff3f5be87bf6552477f9a5d395a068c3/eventlet-0.41.2-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "e91010caa1880bb511de6ce2ed2186ef3493e0762a4d3ee93e97a0fcccdaaa28", - "url": "https://files.pythonhosted.org/packages/8f/03/9562f7aa854e001e3a63034c0a97590a1546e4fe530abf511c2ce07b0cb1/eventlet-0.41.1.tar.gz" + "hash": "721b86b77fca33a735598292022ac6feef99747bf48f52defdada6b572acd5af", + "url": "https://files.pythonhosted.org/packages/eb/e8/6a3a23a3b85ed129b21309c6eca873d833855e63879cd72b9dac20d9b76a/eventlet-0.41.2.tar.gz" } ], "project_name": "eventlet", @@ -1543,14 +1649,14 @@ "build; extra == \"dev\"", "commitizen; extra == \"dev\"", "dnspython>=1.15.0", - "greenlet<3.4", + "greenlet>=1.0", "isort; extra == \"dev\"", "pip-tools; extra == \"dev\"", "pre-commit; extra == \"dev\"", "twine; extra == \"dev\"" ], "requires_python": ">=3.10", - "version": "0.41.1" + "version": "0.41.2" }, { "artifacts": [ @@ -1597,19 +1703,19 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "06e1c6d5e12277022172b7f8d090affe0e8f940f1a37ca45f548855ee51a6b86", - "url": "https://files.pythonhosted.org/packages/e7/20/b0d6da41a08b30faef4f20302f5d826a9d39073750cd9100b463eb840cf4/filelock-3.30.3-py3-none-any.whl" + "hash": "22e58ca3b1ae3b98993b762d7338367ae64fe50252bf78d59da3bfebcdf1cedd", + "url": "https://files.pythonhosted.org/packages/01/a4/9b63d595d748e3aff8812b65eacc1a2c4bd90b7c2012e08e72373b4835eb/filelock-3.32.4-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "6575420cd497ed15fc43a7ac46c48f6b6371c733fc40cf5a4e216871397bc1e2", - "url": "https://files.pythonhosted.org/packages/d6/01/9256cbe36eb2a84501920adad7d0e86738ab0ad60e64f4f5c38d7dd94dc1/filelock-3.30.3.tar.gz" + "hash": "2bde2e4cf732e0153406d8a7bc80620ecf5e621fe0d25e41143c4e3b4733ff30", + "url": "https://files.pythonhosted.org/packages/6d/30/03b03951873a1a0ffc7e8ca0e10c15597b59e8d0e39260704cd2ea087bc4/filelock-3.32.4.tar.gz" } ], "project_name": "filelock", "requires_dists": [], "requires_python": ">=3.10", - "version": "3.30.3" + "version": "3.32.4" }, { "artifacts": [ @@ -1663,90 +1769,90 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "0bf57df54f1c66273bf3601c2a1e41b12138fe848933718369663bc54f177ca2", - "url": "https://files.pythonhosted.org/packages/f9/3c/2fe77ee6e3d381b3c50c0b7d6c4c08c08b8ff5e8c0d9dd51a3b426d61eec/gevent-26.5.0-cp312-cp312-musllinux_1_2_x86_64.whl" + "hash": "2d2c9c23cf68e2d4565505f8b5cd31b269259e1fbfb57bcc3d4d1cd34ee274d1", + "url": "https://files.pythonhosted.org/packages/6d/16/f06ee39db14422d5f749cdcfeb7b8a7b426e087dace9d2c8675633fe6eb4/gevent-26.8.0-cp312-cp312-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "cd3dc60581687e2618286108f8e2f820d8446be4b34131065011c066e911d39c", - "url": "https://files.pythonhosted.org/packages/2a/ea/ea87c08931c9e4c6c40bb05a2cb19c2d6f93fe6e0052f9152ea5ade6d037/gevent-26.5.0-cp311-cp311-manylinux_2_28_aarch64.whl" + "hash": "946848924dd80d29184367c0c9a4fc809411d17afbe157128687df8d7d838560", + "url": "https://files.pythonhosted.org/packages/05/59/967712d5cbabb82fb0619914b82266d0150322966376430df4c73dfbfda4/gevent-26.8.0-cp312-cp312-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "e80ad2a8a1e8bdaa5605e3bf4929e0cebf9ea7b8237c83362f7257698bb14280", - "url": "https://files.pythonhosted.org/packages/37/0b/1a530b2db55c97cc0cf44116201f538f3033c04c1d2aca143979b412f4be/gevent-26.5.0-cp312-cp312-macosx_11_0_universal2.whl" + "hash": "be8810be489005bee66d2c6ad1025bfa4f5cfc46f50c775876b35ffe2acf7e3b", + "url": "https://files.pythonhosted.org/packages/0f/5d/a5e9b1dee7395c68a2d137eb31154cbf5357a54eab19595273145dc69ebf/gevent-26.8.0-cp311-cp311-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "68c5fc21cef80268cdff88a4ae6c025fabb019b071f6f8ee4d20a7bccbddb873", - "url": "https://files.pythonhosted.org/packages/3a/4c/7f5ed67e52dfdef4ff91ae1a6fb28186d52e2496962edc8f17bdea9ab2c0/gevent-26.5.0-cp311-cp311-manylinux_2_28_s390x.whl" + "hash": "108614bd47ec714ed93d141965bac8c17e44bbae08e238696da6261f013caa83", + "url": "https://files.pythonhosted.org/packages/14/99/b82608b486cd9f79a4ebb480779900e7bb7dc095dc9217991fdb173b0084/gevent-26.8.0-cp312-cp312-manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "d325502eb0695708ef8c899f605573ed6847f3961f8159627dba267fbf3ce457", - "url": "https://files.pythonhosted.org/packages/4c/75/0f5da6ca045f8a052203e1810058029f4b682507a789b413cac7d28bae28/gevent-26.5.0-cp311-cp311-manylinux_2_28_x86_64.whl" + "hash": "848c2d1441e9fc5ff5b32aa347a2de561ac3d19abda3d9d61cd0e98aaeb9d8d4", + "url": "https://files.pythonhosted.org/packages/19/ec/739e90f26a7d98fd8f70cf737e7720cb872bc9cd61b2b3954d179f24801d/gevent-26.8.0-cp311-cp311-manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "bccff69c462e3650a0fd1d4e9cfc8b6effe15f3e9b1cad20a7bb5ce14b057efd", - "url": "https://files.pythonhosted.org/packages/4e/69/1559b1f6b5107a9118fccd300240879bd581b6d87b03d568d0d155ea702c/gevent-26.5.0-cp312-cp312-manylinux_2_28_x86_64.whl" + "hash": "b564b959a5391bcfa607bf991a6c368fc0cc7f12730ab0d21777839f643f8285", + "url": "https://files.pythonhosted.org/packages/1f/1d/35af72b6cdc2e48017d92cb44a25623ca32515aef805727eddffb8c60025/gevent-26.8.0-cp311-cp311-manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "a11daf3a588b932c8bf965fb18444c69aff48badec88435e988cf8d67137075a", - "url": "https://files.pythonhosted.org/packages/6e/f1/fcff7f7fad2bb33f3742db6b2145825a2191c0cd31d75789b0741fd28faf/gevent-26.5.0-cp311-cp311-musllinux_1_2_aarch64.whl" + "hash": "5fa5728333b744f5d10bd6fa5474616c7a1a76da81044cfa3d2d0fae2d245ba2", + "url": "https://files.pythonhosted.org/packages/29/20/865715d8b4d723253ef09292358027aa5eae02ca0710aee4664d5b477c4e/gevent-26.8.0-cp310-cp310-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "2ba673dcbf7747513b58fa64ca7e9d6a828bc5c604d1552d23db89006d7911df", - "url": "https://files.pythonhosted.org/packages/72/b7/01a5880e01702f39fb09e3616c624054a0dc9a82561a865f3b1eff4bfc80/gevent-26.5.0-cp310-cp310-manylinux_2_28_x86_64.whl" + "hash": "8e2be34e41557387a422e78c0124e2b90a4df9f1cc87c891503bf34a95ae1ce5", + "url": "https://files.pythonhosted.org/packages/4f/f4/a49ac5d1b508dfcc9af2d08435d9c2c71fdc12d44fe75156c6441f6127de/gevent-26.8.0-cp312-cp312-manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "271b1474d81bb33036631adb16a35e5a1ee9dc414b05c999d6b01dc839a89975", - "url": "https://files.pythonhosted.org/packages/8d/fe/035ec5fa58a886740a744380118f03a90ac2da3f6c9cba248f28074ce40a/gevent-26.5.0-cp310-cp310-musllinux_1_2_x86_64.whl" + "hash": "94544a0f5a1d14b711cd630eab0581a53f3ea89370f8963df9ced62ec6552ca9", + "url": "https://files.pythonhosted.org/packages/56/94/a5d38541f3a9fd77aa8defe8bb66698176be959cebda9c2be00fc5ad9e63/gevent-26.8.0-cp312-cp312-macosx_11_0_universal2.whl" }, { "algorithm": "sha256", - "hash": "1101b5ef82a3fb178550cfd80f32293dc8dd2f3d0828292223ebba29d6f76e33", - "url": "https://files.pythonhosted.org/packages/98/57/151314f00bdc6ba77333febb3e9dc97fdf94d79426559b4fa8332f0c2b6e/gevent-26.5.0-cp311-cp311-musllinux_1_2_x86_64.whl" + "hash": "102087dab60f43f52be74e4be7e42345de27d1968707ded31c785e91d41978b1", + "url": "https://files.pythonhosted.org/packages/ae/9b/cfbc57b758d0435889be2726a5ae1570e6563a24976f4b62e5ecf81b3c3d/gevent-26.8.0-cp312-cp312-manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "96d5e96b1b14a4c1023dcfcc114533217f13febc3b6169254f23fc18d19fee29", - "url": "https://files.pythonhosted.org/packages/9f/b1/5f8a4196113cf7f3fdd987b483f7e6b10c28ea3930c4727e31ba8cce51b6/gevent-26.5.0-cp312-cp312-manylinux_2_28_s390x.whl" + "hash": "0b7684220f4cab1dda07143f4494c6883934bf4378014ce860ccba45d252d82e", + "url": "https://files.pythonhosted.org/packages/b0/fb/c258724be7d5c63812961a7448a2f02be84ba71f272021b9a608aceed685/gevent-26.8.0-cp311-cp311-manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "fe42c037253580a3386fce275f8a2a845e540f5a729916934a732f13d42e72cc", - "url": "https://files.pythonhosted.org/packages/b9/df/32fe851ed5f68493f354e09b19bdebae0de1185be4db0b2988e71e737fd3/gevent-26.5.0-cp312-cp312-manylinux_2_28_aarch64.whl" + "hash": "8e22ce9c9b2fbe498c00665cfa4a598ce8d04dec8ea60047c0c4e2e6ce4ee686", + "url": "https://files.pythonhosted.org/packages/b7/ce/605c5a38e50b3043f2e8c6c0885faf8b37fdaeea3cb714e840db19bf5bda/gevent-26.8.0-cp310-cp310-manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "1655eb04c1e20d71b2aa4a3c7528162dd58ff6cc46a037af1f01f534c80fefba", - "url": "https://files.pythonhosted.org/packages/c4/cb/98aa3a299e2fc4a2372b5d124863e02965b64579ffc29fe54d0641e65b2f/gevent-26.5.0.tar.gz" + "hash": "96039f41bbde6dcd72559e5ffbd408a04f46774b47d991d4cf032da8fa79e5a0", + "url": "https://files.pythonhosted.org/packages/b8/eb/5f2db8013f1a4a6df2c23201f384a066f13ff5764a9f62a608c8a50ac8cc/gevent-26.8.0.tar.gz" }, { "algorithm": "sha256", - "hash": "dc7fa28b2d627f8e87595f39043b6dec71e8e7fb97e685e5506c47cf3ff8cb2e", - "url": "https://files.pythonhosted.org/packages/da/92/1d0e7287ae55700a8d25153ac736896bd9bcc3f85a12d374ef398db4b33c/gevent-26.5.0-cp311-cp311-manylinux_2_28_ppc64le.whl" + "hash": "c2730709f8e894c167415e1d3752d63e6ed40f28b60bdb2ba7bdf973fa29296a", + "url": "https://files.pythonhosted.org/packages/c7/2a/82ab6a41a064792e3df4a9c03e1e90ccf622f8f3352d9a22c1e03e2f159e/gevent-26.8.0-cp312-cp312-manylinux_2_28_s390x.whl" }, { "algorithm": "sha256", - "hash": "f519139354d5ca7625df9ddb1b2ffada885c14abc5b4dbae3682e967ddf79669", - "url": "https://files.pythonhosted.org/packages/e4/32/602c499d54472f64e5cdf6013aeab5ce6aa6fed005387e8b4f2d22f5dc8d/gevent-26.5.0-cp312-cp312-musllinux_1_2_aarch64.whl" + "hash": "304a486b1833a83a028c8c02bbdff62867dc2ee1c6ba654d32d7340ab7b19afb", + "url": "https://files.pythonhosted.org/packages/c9/35/df8f0e37975a4a8d832542316f7abc5107fa3cf516201fd40a7588446b0a/gevent-26.8.0-cp311-cp311-manylinux_2_28_s390x.whl" }, { "algorithm": "sha256", - "hash": "9f463c7d6f69d13b6fe8e3b832a6175a6e95328a940f38495d25496d1ae8ad88", - "url": "https://files.pythonhosted.org/packages/e5/9a/21332674f9a10e8cdf13b41b52e9d663647a1c6e1dc3c62b07c0aeefd360/gevent-26.5.0-cp312-cp312-manylinux_2_28_ppc64le.whl" + "hash": "5a2fae0fe891f7975cc0a5e4ac3b29bb0fc569a271de4b2b8fdb73dccbf7bc8e", + "url": "https://files.pythonhosted.org/packages/de/cc/d4eb977aed853f2fab530b46de99d7ce6ff3a0993ad56251c0feee5db837/gevent-26.8.0-cp311-cp311-musllinux_1_2_aarch64.whl" } ], "project_name": "gevent", "requires_dists": [ - "cffi>=1.17.1; platform_python_implementation == \"CPython\" and extra == \"recommended\"", - "cffi>=1.17.1; platform_python_implementation == \"CPython\" and extra == \"test\"", - "cffi>=1.17.1; platform_python_implementation == \"CPython\" and sys_platform == \"win32\"", + "cffi>=2.1.1; platform_python_implementation == \"CPython\" and extra == \"recommended\"", + "cffi>=2.1.1; platform_python_implementation == \"CPython\" and extra == \"test\"", + "cffi>=2.1.1; platform_python_implementation == \"CPython\" and sys_platform == \"win32\"", "coverage<7.13,>=5.0; sys_platform != \"win32\" and extra == \"test\"", "dnspython<2.0,>=1.16.0; python_version < \"3.10\" and extra == \"dnspython\"", "dnspython<2.0,>=1.16.0; python_version < \"3.10\" and extra == \"recommended\"", @@ -1768,8 +1874,8 @@ "zope.interface", "zope.schema; extra == \"docs\"" ], - "requires_python": ">=3.9", - "version": "26.5.0" + "requires_python": ">=3.10", + "version": "26.8.0" }, { "artifacts": [ @@ -1795,17 +1901,18 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "79a36ee1f83523214a3f72d56cf1c4e490d577dc61af77e43dfe5862bd9da01a", - "url": "https://files.pythonhosted.org/packages/8d/90/04dff7c1e176bb1c3011ef1647393d368790da710d8dde1cdcfad301f45a/gitpython-3.1.52-py3-none-any.whl" + "hash": "8ab28c9da863cdd9e7d7694ec46cf3e6c9a12d8a30a1acd3447aec11975d530c", + "url": "https://files.pythonhosted.org/packages/6f/5e/49cc172da4d0578644ba37cec5cb365b1fefc603b26edea9bcac1c7f830a/gitpython-3.1.61-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "de0a8ad86274c6e75ae8b37dd055ba68f19818c813108642263227b20775b48e", - "url": "https://files.pythonhosted.org/packages/e5/fd/df0bafa4eb5ea2f51e1adee9f7a94c8e62c5d180e65117045dfca3439c8a/gitpython-3.1.52.tar.gz" + "hash": "f51c24d8c0f733a195447385f5774a5dfe8767f5acfd7994a33755644c6ecc95", + "url": "https://files.pythonhosted.org/packages/6f/61/3285044215fb596bf093e39ccb96ece0a1076a8ca57a61e069a6a33cdb1b/gitpython-3.1.61.tar.gz" } ], "project_name": "gitpython", "requires_dists": [ + "basedpyright==1.39.9; (python_version >= \"3.9\" and sys_platform != \"cygwin\") and extra == \"test\"", "coverage[toml]; extra == \"test\"", "ddt!=1.4.3,>=1.1.1; extra == \"test\"", "gitdb<5,>=4.0.1", @@ -1824,7 +1931,7 @@ "typing-extensions>=3.10.0.2; python_version < \"3.10\"" ], "requires_python": ">=3.7", - "version": "3.1.52" + "version": "3.1.61" }, { "artifacts": [ @@ -1863,113 +1970,128 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", - "url": "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl" + "hash": "12e2ee66c2aba86133f10fd99d6a8856c6d351ffb7be0e4d52ef2cc5fbb705b2", + "url": "https://files.pythonhosted.org/packages/b8/d7/734e5f198888876b42d7616ff6644c075baf6b8a2412deadd6b0e1b8b20c/greenlet-3.5.5-cp312-cp312-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "816230f469381ad0a43abc9fa8dda5a699e32fb78958dde32ded93213b70a667", + "url": "https://files.pythonhosted.org/packages/07/67/07ecd6d85c0f253363cbc4ac9e7dd048ca571a267fbccfea084d6009ac3b/greenlet-3.5.5-cp310-cp310-macosx_11_0_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "adb4bae02e91a8e863e48b177e4014bdcac8a6b5e047ea1df687a61534b85e6c", + "url": "https://files.pythonhosted.org/packages/0b/d8/7cc97c142388aef03f622e001c572c4f84e9252a439549d483f555771970/greenlet-3.5.5.tar.gz" + }, + { + "algorithm": "sha256", + "hash": "c69bed34470abfcd456984fdadaa18e62169af4480335c45f3c32d1d9c12e638", + "url": "https://files.pythonhosted.org/packages/18/5e/58c561efde575c4ca070030e2e0513e8d1b07b76d8a2d84a9bcef1becd42/greenlet-3.5.5-cp310-cp310-manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "8c1fdd7d1b309ff0da81d60a9688a8bd044ac4e18b250320a96fc68d31c209ca", - "url": "https://files.pythonhosted.org/packages/00/68/91f061a926abead128fe1a87f0b453ccf07368666bd59ffa46016627a930/greenlet-3.3.2-cp310-cp310-musllinux_1_2_x86_64.whl" + "hash": "a5433cf291e0ef9114bd14d0d824db6e5e4a43033234bca48181a9597acca07b", + "url": "https://files.pythonhosted.org/packages/24/35/426733bc24247ee17bb76df90f550c299ff5f8572b2bdd7cacb5c53fd994/greenlet-3.5.5-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "508c7f01f1791fbc8e011bd508f6794cb95397fdb198a46cb6635eb5b78d85a7", - "url": "https://files.pythonhosted.org/packages/03/5f/6e2a7d80c353587751ef3d44bb947f0565ec008a2e0927821c007e96d3a7/greenlet-3.3.2-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl" + "hash": "49520f0c95a48b42cf55414b8e8479beb274ea70431afc33e3f79903c71f4380", + "url": "https://files.pythonhosted.org/packages/2e/7e/9ecd0285e3153532ae07aeb88063c43c72b4221cf0d4d123b02f3682e3ff/greenlet-3.5.5-cp312-cp312-macosx_11_0_universal2.whl" }, { "algorithm": "sha256", - "hash": "97245cc10e5515dbc8c3104b2928f7f02b6813002770cfaffaf9a6e0fc2b94ef", - "url": "https://files.pythonhosted.org/packages/24/b4/21f5455773d37f94b866eb3cf5caed88d6cea6dd2c6e1f9c34f463cba3ec/greenlet-3.3.2-cp310-cp310-musllinux_1_2_aarch64.whl" + "hash": "55272212cbc5f43d1d723725ab931f1939969b7e9523882ca58b55061769d053", + "url": "https://files.pythonhosted.org/packages/35/73/60e4bbcc89252037b18087f2ec16405d5b2d5be42dde191bbf3667e96102/greenlet-3.5.5-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "9bc885b89709d901859cf95179ec9f6bb67a3d2bb1f0e88456461bd4b7f8fd0d", - "url": "https://files.pythonhosted.org/packages/38/3f/9859f655d11901e7b2996c6e3d33e0caa9a1d4572c3bc61ed0faa64b2f4c/greenlet-3.3.2-cp310-cp310-macosx_11_0_universal2.whl" + "hash": "abc8bc8d9f935cd685457545b6a53863a877fdc12c2c0f5ee9beee18d9db139c", + "url": "https://files.pythonhosted.org/packages/39/82/5c873dbb4fb001d22fbbd50e80d4c1b0181ddae106856132160f84b94e88/greenlet-3.5.5-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl" }, { "algorithm": "sha256", - "hash": "43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", - "url": "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl" + "hash": "523bb8e27614d77101ea7a8cf59f8d91219b72d5c29f6a038c92b50828bfa8d0", + "url": "https://files.pythonhosted.org/packages/3c/78/52de4f7ac9152ad1dbc8f437895c1e573d30ee1e1427e0f91a280e2417b6/greenlet-3.5.5-cp310-cp310-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be", - "url": "https://files.pythonhosted.org/packages/5a/00/32d30dee8389dc36d42170a9c66217757289e2afb0de59a3565260f38373/greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl" + "hash": "6d9b454c5fc48aeaa7c4337813dbf513a6870468e426438a04d922c6d0fe63db", + "url": "https://files.pythonhosted.org/packages/3e/4a/92fc51d5d35912f4f06eec037ba347985defd0be47463a010a325634d9d2/greenlet-3.5.5-cp311-cp311-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb", - "url": "https://files.pythonhosted.org/packages/5c/c5/cc09412a29e43406eba18d61c70baa936e299bc27e074e2be3806ed29098/greenlet-3.3.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl" + "hash": "be63afcbbccfad3dd95a1ba12ada84dab2ef32031973d80b5b92df67fa763a61", + "url": "https://files.pythonhosted.org/packages/4e/a3/07297917485ee2ca85bc3c8dc6ed85ad3fffcf424047fba62671dba68e97/greenlet-3.5.5-cp311-cp311-macosx_11_0_universal2.whl" }, { "algorithm": "sha256", - "hash": "442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99", - "url": "https://files.pythonhosted.org/packages/70/79/0de5e62b873e08fe3cef7dbe84e5c4bc0e8ed0c7ff131bccb8405cd107c8/greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl" + "hash": "74cc6df89ec5302337adc9cf096221cbed2510fd444b0e0f1586cf0470740864", + "url": "https://files.pythonhosted.org/packages/51/2d/f2c928218ac52f26d7a2c188c171d1b7e728b23782cb3347e7b4fce1493a/greenlet-3.5.5-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358", - "url": "https://files.pythonhosted.org/packages/72/83/3e06a52aca8128bdd4dcd67e932b809e76a96ab8c232a8b025b2850264c5/greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl" + "hash": "9ff00e12102358292087274dfb1669132387ff6e7920ebf9d85f4826ce0d3a56", + "url": "https://files.pythonhosted.org/packages/51/91/00b3c0566316c6f383ea16ee05388ec4f57f6e0afa49e72ae471eda8c47f/greenlet-3.5.5-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2", - "url": "https://files.pythonhosted.org/packages/9c/8b/1430a04657735a3f23116c2e0d5eb10220928846e4537a938a41b350bed6/greenlet-3.3.2-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl" + "hash": "1b5ed9162c0c098e0bbc2cf88a94f433c1b8926f831745252e099e5d83e17759", + "url": "https://files.pythonhosted.org/packages/63/2c/eb487fafc9f50ffff2b1e0b697f70fb34bf150821c08ab225aacf5583a7e/greenlet-3.5.5-cp312-cp312-manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", - "url": "https://files.pythonhosted.org/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz" + "hash": "3134291427bb0f3526e9d90311988caf336eb43730e95244997a4fb15f45144f", + "url": "https://files.pythonhosted.org/packages/70/12/f7df98e72a8eb4a7edfec5a08d6d1a4ab53a52c95ba0b2ea6c10b8dd9bd0/greenlet-3.5.5-cp311-cp311-manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f", - "url": "https://files.pythonhosted.org/packages/a3/90/42762b77a5b6aa96cd8c0e80612663d39211e8ae8a6cd47c7f1249a66262/greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl" + "hash": "86c5113d698cb8d927b2750bb1f1d59eefe3a37e0e0217491aee29a7f84ef52c", + "url": "https://files.pythonhosted.org/packages/74/3f/b31e6bd6adb8f80492efb6a47e8f9cd0e7335db5aac2795b1876d5afcfee/greenlet-3.5.5-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl" }, { "algorithm": "sha256", - "hash": "3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", - "url": "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl" + "hash": "147b25a42e5ca5be3d42356e8f608b37af715a1c196e9bf9d1627f3341adfe1d", + "url": "https://files.pythonhosted.org/packages/78/ac/5c5b959999b6f09c3026b5dfe171575bc3121c5236ce74f495096f25b203/greenlet-3.5.5-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "ad0c8917dd42a819fe77e6bdfcb84e3379c0de956469301d9fd36427a1ca501f", - "url": "https://files.pythonhosted.org/packages/ad/55/9f1ebb5a825215fadcc0f7d5073f6e79e3007e3282b14b22d6aba7ca6cb8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl" + "hash": "6ca5d6ae0739e5764f2cfcfaa562ac5a990cbdaedca93251c5e3cf07c362371f", + "url": "https://files.pythonhosted.org/packages/9b/30/87c212b5c684d0e72974f1063b7a9687631e8985902c06e1016542c874e7/greenlet-3.5.5-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl" }, { "algorithm": "sha256", - "hash": "a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55", - "url": "https://files.pythonhosted.org/packages/bf/6f/f3d64f4fa0a9c7b5c5b3c810ff1df614540d5aa7d519261b53fba55d4df9/greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl" + "hash": "19d59f068887d8c5907fc177f27683413ace3011b6ed646c0b309266e74a6502", + "url": "https://files.pythonhosted.org/packages/9c/dc/17d3a5acceb2fd0bbc9682a228117b719cdfb68085e6dbb33fa325755f27/greenlet-3.5.5-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", - "url": "https://files.pythonhosted.org/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl" + "hash": "655bca754a2ef4efcb0eb48a94d3f4593536d0f3d48f8ed44343c01d16a92f95", + "url": "https://files.pythonhosted.org/packages/a4/17/cd5134be659cd4a443e7a61ae670dabec165a814c51162916d637b6dd38e/greenlet-3.5.5-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "527fec58dc9f90efd594b9b700662ed3fb2493c2122067ac9c740d98080a620e", - "url": "https://files.pythonhosted.org/packages/ed/45/67922992b3a152f726163b19f890a85129a992f39607a2a53155de3448b8/greenlet-3.3.2-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl" + "hash": "03551ed792cb1b4fc0277a0c60dfd8c343894a0ba06fe60dcd22f568b433da39", + "url": "https://files.pythonhosted.org/packages/ac/58/ed98b80ac5738c149a5258544843c45601ade1fd70f61740cdaead6351b3/greenlet-3.5.5-cp311-cp311-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86", - "url": "https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl" + "hash": "27493374cff1d1b7919dc8126547f2aea582737e3046147b434b1e12de56389b", + "url": "https://files.pythonhosted.org/packages/c8/8b/6acf112ed8aee499f25b4d6949820fb02ac950ff9c1f3d793bd5be0599f2/greenlet-3.5.5-cp312-cp312-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", - "url": "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl" + "hash": "35cbb8bf55ace57fbccb4fb8622c4521713acd8691e77f4696d416ea7ca527da", + "url": "https://files.pythonhosted.org/packages/d8/c0/b27589e25d220289edcd4d582b2b17b83058d1a56d53d971b6ea1a34f10d/greenlet-3.5.5-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "b568183cf65b94919be4438dc28416b234b678c608cafac8874dfeeb2a9bbe13", - "url": "https://files.pythonhosted.org/packages/fb/07/cb284a8b5c6498dbd7cba35d31380bb123d7dceaa7907f606c8ff5993cbf/greenlet-3.3.2-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl" + "hash": "8a268024ce2d7d2b04694bf1594058981a9fa663d1df4b762dee499211ed7c1c", + "url": "https://files.pythonhosted.org/packages/db/51/6f732f9314cda54c5fd48a7620c7160f4f286967e8045ad94b9d66ce80b7/greenlet-3.5.5-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", - "url": "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl" + "hash": "f1e2db190db51c17433eee424803818cf0670bf049d9cfe0dd07be111d1aa7c4", + "url": "https://files.pythonhosted.org/packages/f6/ff/c3855a00c2417e8f61c7b9f0bc4f8f599c6928f5c15681ad251e78a91e05/greenlet-3.5.5-cp310-cp310-musllinux_1_2_x86_64.whl" } ], "project_name": "greenlet", @@ -1981,7 +2103,7 @@ "setuptools; extra == \"test\"" ], "requires_python": ">=3.10", - "version": "3.3.2" + "version": "3.5.5" }, { "artifacts": [ @@ -2060,35 +2182,38 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", - "url": "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl" + "hash": "815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4", + "url": "https://files.pythonhosted.org/packages/57/b0/0e52c878c53f245edd3a11020f20979b3f490f245af532c7cae3027754b5/idna-3.19-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", - "url": "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz" + "hash": "5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15", + "url": "https://files.pythonhosted.org/packages/5f/f7/abb373e5757eaec4b922b92f97ec8d6d7e057cf06778247604fbc4e7c3f3/idna-3.19.tar.gz" } ], "project_name": "idna", "requires_dists": [ + "coverage>=7.10.0; extra == \"all\"", + "hypothesis>=6.141.1; extra == \"all\"", "mypy>=1.11.2; extra == \"all\"", "pytest>=8.3.2; extra == \"all\"", - "ruff>=0.6.2; extra == \"all\"" + "ruff>=0.16.0; extra == \"all\"", + "ty>=0.0.37; extra == \"all\"" ], "requires_python": ">=3.9", - "version": "3.18" + "version": "3.19" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7", - "url": "https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl" + "hash": "bba5600596a7e21f3eef53281cf28d6a5195634d2f2b78ff9501a3272c6eaab0", + "url": "https://files.pythonhosted.org/packages/b3/55/ecca97ae19075f1fac62def77731e7f535e6c1fb8f92ff08160c5e6dade8/importlib_metadata-9.0.1-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", - "url": "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz" + "hash": "ab830580bc0ef3db61ce8fae716389e5462b67e033018bab6d8f80ef17172f99", + "url": "https://files.pythonhosted.org/packages/6f/7e/1e7e8dc30634b93ebb3d58a3dea569ad146e656218d3960ab04f62047b29/importlib_metadata-9.0.1.tar.gz" } ], "project_name": "importlib-metadata", @@ -2104,7 +2229,7 @@ "pytest-cov; extra == \"cover\"", "pytest-enabler>=3.4; extra == \"enabler\"", "pytest-mypy>=1.0.1; platform_python_implementation != \"PyPy\" and extra == \"type\"", - "pytest-perf>=0.9.2; extra == \"test\"", + "pytest-perf>=0.17; extra == \"test\"", "pytest-ruff>=0.2.1; sys_platform != \"cygwin\" and extra == \"check\"", "rst.linker>=1.9; extra == \"doc\"", "sphinx-lint; extra == \"doc\"", @@ -2112,7 +2237,7 @@ "zipp>=3.20" ], "requires_python": ">=3.10", - "version": "9.0.0" + "version": "9.0.1" }, { "artifacts": [ @@ -2751,134 +2876,134 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "f02cf17a6ca1abe29b5f980644f7551f94d71f2011509b26d8625ce038f0df64", - "url": "https://files.pythonhosted.org/packages/17/dd/fa8bd265110dfa51c20cb529f9e6d240a16fafe7e645004c6af2d01353ba/msgpack-1.2.1-cp312-cp312-musllinux_1_2_x86_64.whl" + "hash": "4b554d8164ebb526892194f71dcd96ef1fefe0c250087498785d3ffc04a80be3", + "url": "https://files.pythonhosted.org/packages/be/af/91b0d8d3fb3063e259daee3ea8515cea6282f68f4b0e5f0b6fea25762c6e/msgpack-1.2.2-cp312-cp312-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "a28d076ca7c82b9c8728ad90b7147489449557038bed50e4241eb832395169b4", - "url": "https://files.pythonhosted.org/packages/03/8d/671d81534ea0e2b0e8a121be100020da09eb78861fe3aa8f3ef7dcd3bed1/msgpack-1.2.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + "hash": "a9b4cf3685a135666d27d0d7a73fece74e2fad01d9b508fded89e843512f0e90", + "url": "https://files.pythonhosted.org/packages/03/f7/a6bf145f7d3eb734b3d97fa295f8007a586799ef56b456c8b27bff62caac/msgpack-1.2.2-cp310-cp310-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "67f6dd22fa72a93752643f07889796d62739a13415ee630169a8ce764f86cf9f", - "url": "https://files.pythonhosted.org/packages/09/11/f8563e471093420cf6478cb3271a0175d8402b82d879783d4035d2d03360/msgpack-1.2.1-cp311-cp311-musllinux_1_2_x86_64.whl" + "hash": "46ec851571d8f1b6e29794ebb9dd36f785008da6d14f57c702e60781d6caf648", + "url": "https://files.pythonhosted.org/packages/0e/03/c1e0035e1f923f548b7016a2fef5afea431cdae95d397c5aa52ed75dde05/msgpack-1.2.2-cp310-cp310-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "aded5bdf32609dc7987a49bbbd15a8ef096193f96dd8bbeb791de729e650acf5", - "url": "https://files.pythonhosted.org/packages/0c/3a/dd518a1bf78ed1e9ad8afe57307c079a00eafe4b3068932a27ca1ea56b4f/msgpack-1.2.1-cp311-cp311-macosx_11_0_arm64.whl" + "hash": "7fe374ba76eb0ecca13a1703daa8fa85825a6ddddbb52d4c1a732fa524194683", + "url": "https://files.pythonhosted.org/packages/0f/df/e20bcf5c149890545334743b212eb4b82e1a25fe0a34f99753a1755bfab5/msgpack-1.2.2-cp311-cp311-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "1dabedcd0f23559f3596428c6589c1cd8c6eaed3a0d720795b07b0225d769203", - "url": "https://files.pythonhosted.org/packages/19/9f/a70c9cb1a04ecc134005149367dcfe35d167284e8f65035a1e4156ad17b5/msgpack-1.2.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "86f173a584f72f6164801f31866d22a581f60c991572cf922aed9ab8eb422b77", + "url": "https://files.pythonhosted.org/packages/17/92/d91a08a913a3bbafbede5b9dbf48e4e517f7d92510877b6b02730060ea85/msgpack-1.2.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "60926b75d00c8e816ef98f3034f484a8bc64242d66839cef4cf7e503142316a0", - "url": "https://files.pythonhosted.org/packages/26/aa/753ad8b007b464e1d8aa0c8e650b9c5f4f725e658fc5ac8a7635c55b7f6e/msgpack-1.2.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "42fd9260416885b4815caca5bdd14dfd5dda6cdade732d6c09104ef8f6228761", + "url": "https://files.pythonhosted.org/packages/2a/fd/d8533fed473cc3e309a701e851d0e5fe36ada5552a3899025f5c69fbe877/msgpack-1.2.2-cp311-cp311-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "04c721c2c7448767e9e3f2520a475663d8ee0f09c31890f6d2bd70fd636a9647", - "url": "https://files.pythonhosted.org/packages/31/f9/c0a1c127f9049db9155afc316952ea571720dd01833ff5e4d7e8e6352dbb/msgpack-1.2.1.tar.gz" + "hash": "4a4348705be86e029d04e741cf9ed0dfe03e942d7d3b92e838fa80d3aa2c3ebc", + "url": "https://files.pythonhosted.org/packages/31/78/90c15bebb1a72667349ca62d4507e9d9369e7f8f76b95f490b823d3622e5/msgpack-1.2.2-cp312-cp312-macosx_10_13_x86_64.whl" }, { "algorithm": "sha256", - "hash": "350cb813d0af6e65d2f7ef0d729f7ff5be5a8bce03665892f43e5883d4ecc1b8", - "url": "https://files.pythonhosted.org/packages/3d/dc/9ebe654a73c3aed2e40aa6b52e3c2a02b5f53ef0085fa235a45d5b367f87/msgpack-1.2.1-cp310-cp310-musllinux_1_2_aarch64.whl" + "hash": "58ce37a4a54577115922385d37201d9a44d66d0167dfbbf4770a2e9bf8ea7ba3", + "url": "https://files.pythonhosted.org/packages/3f/f5/ae93f85b063d744731cb285528210cd950333b167cacc7b2f96e1420a475/msgpack-1.2.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "ee1d9ed27d0497b848923746cf762ed2e7db24f4be7eec8e5cbe8c766aa707b7", - "url": "https://files.pythonhosted.org/packages/42/eb/b67cf64218a2fa25e1c671fe1d3dbb06cbeb973e71bc4b822da079862d0b/msgpack-1.2.1-cp310-cp310-musllinux_1_2_riscv64.whl" + "hash": "77c2e018417dc1d66f235e383877ee885b60ade9d29e494dd581e08af2cb1923", + "url": "https://files.pythonhosted.org/packages/41/f0/29f591bea185616cf417645ac03bd3ad9b317483ad8572160e325f7fe777/msgpack-1.2.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "d3567748a5107cb40cdf66a275430c2f87c07777698f4bfd25c35f44d533258c", - "url": "https://files.pythonhosted.org/packages/50/2e/ebdb85a8da151397a2790363676b7ed7c125924fe618e4c6d8befb0cc62c/msgpack-1.2.1-cp312-cp312-macosx_11_0_arm64.whl" + "hash": "9bd3d1557c3fe1a095068210708a03e3e4795973392af6f4047060e70abd9a6c", + "url": "https://files.pythonhosted.org/packages/44/92/541e9fa4623587767623788b38d11fc78d402acb1421962a13d3ace48bef/msgpack-1.2.2-cp310-cp310-musllinux_1_2_riscv64.whl" }, { "algorithm": "sha256", - "hash": "4202c74688ca06591f78cb18988228bd4cca2cc75d57b60008372892d2f1e6e6", - "url": "https://files.pythonhosted.org/packages/5a/cc/85039b7b0eb168aaad7383a23c97e291a11f08351cb45a606ce865e4e3f1/msgpack-1.2.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + "hash": "0e91332144f69bc3018c91232fac26da580ef748fb8eaddd7914d4458001cc4f", + "url": "https://files.pythonhosted.org/packages/4e/8e/c70c8c9180c5ddf4440eb8658ebead98e22e7686fbf84f6b165031430750/msgpack-1.2.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "8c7b398c56ff125feae96c2737abfec5595f1fa0aa186df60c56040b8accb95c", - "url": "https://files.pythonhosted.org/packages/5b/16/f70100614b69feb3ade7285f08c9c52d6cda0a5c03f3f5e2facd63acb211/msgpack-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl" + "hash": "3e915d390d7068b257ca8b62f3fc59fad135c8631d1017ab03b0b924b07c5367", + "url": "https://files.pythonhosted.org/packages/50/9a/f10ce11fa62700c9ab87a22e65b9ca272f7f673ddd31aeb2de6ae272ad35/msgpack-1.2.2-cp312-cp312-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "020e881a764b20d8d7ca1a54fc01b8175519d108e3c3f194fddc200bda95951a", - "url": "https://files.pythonhosted.org/packages/6a/fd/6adabd4f6d5e686f97dd02ce7fce3fe4cf672cbac36b8f67ff4040e8ad8b/msgpack-1.2.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + "hash": "9d7fb25b4442fae0cb2590272d06ab4f6caa526ee36a994edb81e946b874813e", + "url": "https://files.pythonhosted.org/packages/53/fd/a05ba8f84c5951c9aec2a19c1c81f6c4a67b8bec80af604ac5b23ccfa019/msgpack-1.2.2-cp311-cp311-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "146ee4e9ce80b365c6d4c47073da9da7bcec473e58194ceee5dd7620ace77e06", - "url": "https://files.pythonhosted.org/packages/70/e0/7ba9e1542bf0771a27b8b37c1316e3f95ae9d748fd765284655c476ad4ef/msgpack-1.2.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "9eb0b0e602064527a045ea28c4f174ed69383587e29cebe28947e3b84106eb2a", + "url": "https://files.pythonhosted.org/packages/6d/44/ea2100ec54d30c46ee9dba10a3bfb79b655e96c6df237238a3234c75869b/msgpack-1.2.2.tar.gz" }, { "algorithm": "sha256", - "hash": "779197a6513bab3c3632265e3d0f7cb3227e62510841a6f34f1eaa37efbb345e", - "url": "https://files.pythonhosted.org/packages/70/ff/59aa3887b860bbf43532835e192b1c388a17590d6068ae4f8b2bc74c906e/msgpack-1.2.1-cp311-cp311-musllinux_1_2_riscv64.whl" + "hash": "c522420d78db2431887d45b518e304d86e27b9ad0b30f24e3806a6ad5d8bdbfc", + "url": "https://files.pythonhosted.org/packages/82/fe/d7be978456ff8552e69a8e270d882e7530e01513c096b293d83df03753ea/msgpack-1.2.2-cp312-cp312-musllinux_1_2_riscv64.whl" }, { "algorithm": "sha256", - "hash": "01e2dd6c9b19d333a00282330cc8a73d38d8dabc306dc5b42cd668c3ac82e833", - "url": "https://files.pythonhosted.org/packages/79/70/fb7668ce0386819303047057aef6fc1da73b584291d9cff82b821744e2ef/msgpack-1.2.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + "hash": "0a652ceeededf71d3fa40c303a02a149d42338d310162367b91c539d4bd6e0a3", + "url": "https://files.pythonhosted.org/packages/88/88/c2b6d8e81571da87aa232c0e34a3f3a0e618e6235892065ec82d1d81fc7a/msgpack-1.2.2-cp312-cp312-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "633727297ed063441fd1cda2288865487f33ad14eeb8831afb5f0c396a62cfce", - "url": "https://files.pythonhosted.org/packages/a2/2e/9ee200cde32fd1a0101b4006202fde554c1860adfb9bf7bff31ea4c08df8/msgpack-1.2.1-cp310-cp310-musllinux_1_2_x86_64.whl" + "hash": "f11e09f10210a91c169e39c7a5a1f9090eaa73ad75555fafad5023c3053c47ba", + "url": "https://files.pythonhosted.org/packages/93/15/17374efe9793f5332c7d4727ab40539f95a1dc9df653531795daca8c4281/msgpack-1.2.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "e4f1d0f8f98ade9634e01fb704a408f9336c0a8f1117b369f5db83dc7551d8b1", - "url": "https://files.pythonhosted.org/packages/a6/df/8e2ac970c8f99264cd9997d1c73df5466bc19da3301d7dc5500862a9b089/msgpack-1.2.1-cp312-cp312-musllinux_1_2_riscv64.whl" + "hash": "4710d881d8fb047deed2485707409116722af2b992d3fefd73c7667c4e350839", + "url": "https://files.pythonhosted.org/packages/93/1a/77d32a60a80ee67016e77b13bec07e85f1929a92f046da044591c7eb01c9/msgpack-1.2.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "8ff92d7feeaf5bc26c51495b69e2f99ed97ab79346fb6555f44be7dd2ac6503b", - "url": "https://files.pythonhosted.org/packages/a8/46/62ed8c2e87d7021eab19921594d961ef3aa3794eec76c716dc30f3bfd433/msgpack-1.2.1-cp311-cp311-musllinux_1_2_aarch64.whl" + "hash": "b9b0c1f2aa7b0026b4bd50718100e8b04175e4f36e160aa852502377b5e572e7", + "url": "https://files.pythonhosted.org/packages/ac/c3/00dcd902d66a641b9ba350783feb482ea5c1ca4a7ff6629db0c10c0ea982/msgpack-1.2.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "2ef59c659f289eddf8aa6623823f19fa2f40a4029266889eac7a2505dd210c35", - "url": "https://files.pythonhosted.org/packages/bc/dd/9e8cbd8f5582ca4b590336f2b91ee5662f6a6ca562b565abaf696a0f81ff/msgpack-1.2.1-cp312-cp312-macosx_10_13_x86_64.whl" + "hash": "e05a94a0442de86818a30281c6cc2cb9cc7aa148386fd3541c4d4774b73cb3a9", + "url": "https://files.pythonhosted.org/packages/b5/a4/27400c101a96115fe8484cad57b6c5eb4f9ffba080a1d9be62ec9174bf67/msgpack-1.2.2-cp310-cp310-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "7d31c0ac0c640f877804c67cb2bc9f4e23dc2db97e96c2e67fa27d38283b41f8", - "url": "https://files.pythonhosted.org/packages/d2/b6/e5c737515ed1f166664b87601b532f58cbb73d8aa6a90b99f7c2c5037e8e/msgpack-1.2.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + "hash": "7afa5431f6f3487c584187ca6c8e2a34e9b106529893b3e720eabb068f6ac970", + "url": "https://files.pythonhosted.org/packages/c9/40/181c8944b28f779ed0b2587d24cc0ccf1bc87248204105327140aa20d63d/msgpack-1.2.2-cp310-cp310-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "1548006a91aa93c5da81f3bdcebc1a0d10cea2d25969754fbe848da622b2b895", - "url": "https://files.pythonhosted.org/packages/e4/3c/08ecd5cdfe4e2de43aec79062028ad0f7b2d9b1fea5430068c198ba570da/msgpack-1.2.1-cp310-cp310-macosx_11_0_arm64.whl" + "hash": "8b1415d02e9bf722672af8a90f90813265a0cd0b14163187261e54a5592bc949", + "url": "https://files.pythonhosted.org/packages/d3/af/2b567d684f912fedcefe3f7c37de604716ffa99336bd432688f9f040df92/msgpack-1.2.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "8b267ce94efb76fbd1b3373511420074ee3187f0f7811bf394531de13294735a", - "url": "https://files.pythonhosted.org/packages/ed/bf/35963899493b32030c85fc513b723ae66144ac70c11ebc52e889e16e3d99/msgpack-1.2.1-cp312-cp312-musllinux_1_2_aarch64.whl" + "hash": "336525cc2688e43ea77dfb1a4ce012c8cde561835913801dbfcfdcf4111d8abb", + "url": "https://files.pythonhosted.org/packages/d6/1b/57906337bfee0ead554571dc203ea17c3fad26d51e5eca6271ecd983f73b/msgpack-1.2.2-cp311-cp311-musllinux_1_2_riscv64.whl" }, { "algorithm": "sha256", - "hash": "29a3f6e9667868429d8240dfd063ea5ffdc1321c13d783aa23827a38de0dcb22", - "url": "https://files.pythonhosted.org/packages/f4/6b/e9b1cdc042c4458801d2545ed782a95f3d6ba8e270cce8745b8603c7f748/msgpack-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl" + "hash": "90986cc9aab9d7d1d8f38bcbf65d3f7ac83bdd90c35765db7d691b4829698cba", + "url": "https://files.pythonhosted.org/packages/da/c0/d3ede9f5d16acb4c05a9281859f1e99ef9f877a928eb78454c37f70db001/msgpack-1.2.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "83efa1c898e0fc5380fc0cabbf75164c52e3b5cbb45973710d75821928380c73", - "url": "https://files.pythonhosted.org/packages/fa/7f/5ce020168cf0439041526e95aa068c722c016aee21624e331aeabeee2e8e/msgpack-1.2.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + "hash": "cdb6cc6e1127d15879c47a8b3270716243da82d3e7feab1f5946872c75b3d60f", + "url": "https://files.pythonhosted.org/packages/de/0f/5d1e6d68e516621697a9262b24917d678793e838cf3f331ed4656b3e959d/msgpack-1.2.2-cp311-cp311-musllinux_1_2_x86_64.whl" } ], "project_name": "msgpack", "requires_dists": [], "requires_python": ">=3.10", - "version": "1.2.1" + "version": "1.2.2" }, { "artifacts": [ @@ -2995,195 +3120,135 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "71e63adb0e1f1ed5d9e168f50a91ceb93ae6420731d222dc7da5c69409aa47aa", - "url": "https://files.pythonhosted.org/packages/df/e5/4d2d8af06f788329b4f78f8cc3679bb395392fcaa1e4d8d3c33e85308fa4/orjson-3.11.9-cp312-cp312-musllinux_1_2_x86_64.whl" - }, - { - "algorithm": "sha256", - "hash": "03db380e3780fa0015ed776a90f20e8e20bb11dde13b216ce19e5718e3dfba62", - "url": "https://files.pythonhosted.org/packages/05/94/b0d27090ea8a2095db3c2bd1b1c96f96f19bbb494d7fef33130e846e613d/orjson-3.11.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" - }, - { - "algorithm": "sha256", - "hash": "87e4d4ab280b0c87424d47695bec2182caf8cfc17879ea78dab76680194abc13", - "url": "https://files.pythonhosted.org/packages/09/d3/c5824260ca8b9d7ba82648d042a3f8f4815d18c15bb98a1f30edd1bb2d83/orjson-3.11.9-cp310-cp310-musllinux_1_2_armv7l.whl" + "hash": "532ff8cd4bd59a327a953a7dcde922c7fc25b85e29721bb8633265430d3a3873", + "url": "https://files.pythonhosted.org/packages/71/93/4d71f2df314a97ff0d27a4559bf5888fc8406e3c6dec90e92291e3511215/orjson-3.12.0-cp312-cp312-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "33d7d766701847dc6729846362dc27895d2f2d2251264f9d10e7cb9878194877", - "url": "https://files.pythonhosted.org/packages/09/eb/75d50c29c05b8054013e221e598820a365c8e64065312e75e202ed880709/orjson-3.11.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl" + "hash": "a791f793b287bbc135b8e87c34e35c8bfc693e2a8a620fab1ae682b925f9a32e", + "url": "https://files.pythonhosted.org/packages/04/d1/3b2038ed168d22e14182ed715d6963f9c073a83a2ba43cfe918a4fc43c64/orjson-3.12.0-cp311-cp311-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "0b34789fa0da61cf7bef0546b09c738fb195331e017e477096d129e9105ab03d", - "url": "https://files.pythonhosted.org/packages/0b/ee/66154baf69f71c7164a268a5e888908aec5a0819d13c81d5e2755a257758/orjson-3.11.9-cp310-cp310-musllinux_1_2_aarch64.whl" + "hash": "a696529ec96a90d9a5f9570207efe403c8b08f8e4aa2783ee3403511e2fdfa10", + "url": "https://files.pythonhosted.org/packages/05/34/c2eb3b2900e5597db7841a4c6416ac2d90081bd956b02d4dd1833fa2b96b/orjson-3.12.0-cp311-cp311-macosx_15_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "3513550321f8c8c811a7c3297b8a630e82dc08e4c10216d07703c997776236cd", - "url": "https://files.pythonhosted.org/packages/0e/30/3178eb16f3221aeef068b6f1f1ebe05f656ea5c6dffe9f6c917329fe17a3/orjson-3.11.9-cp311-cp311-musllinux_1_2_aarch64.whl" + "hash": "d14203fb1aae2ad9b3d52f8a0e82aeb10197ef1c9bc61da7f358bd70b00123d5", + "url": "https://files.pythonhosted.org/packages/0f/f3/742fb1f62b825f2c010697eaf4e828004bc2a81e7e806666989c132c7c42/orjson-3.12.0.tar.gz" }, { "algorithm": "sha256", - "hash": "be4fa4f0af7fa18951f7ab3fc2148e223af211bf03f59e1c6034ec3f97f21d61", - "url": "https://files.pythonhosted.org/packages/0e/a4/82b7a2fe5d8a67a59ed831b24d59a3d46ea7d207b66e1602d376541d94a6/orjson-3.11.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "18a87929f31d94a77f7dc93cf527e91f39ce7fe7813d588a4de2507efd32a387", + "url": "https://files.pythonhosted.org/packages/14/6d/e3a8c34d687895aecd8b267a01c46106eb98d8424a83bfa7bacb723854f6/orjson-3.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "135869ef917b8704ea0a94e01620e0c05021c15c52036e4663baffe75e72f8ce", - "url": "https://files.pythonhosted.org/packages/10/5d/b95ca542a001135cc250a49370f282f578c8f4e46cc8617d73775297eea8/orjson-3.11.9-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl" + "hash": "bc7a872f03522d90e0429e6c0c5cd23084f767bedcb4c58048eec19294613344", + "url": "https://files.pythonhosted.org/packages/15/12/644cbbcabb26df61d9ef0c66e6f2bf8b687cc7b66137597f2858951f1952/orjson-3.12.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl" }, { "algorithm": "sha256", - "hash": "231742b4a11dad8d5380a435962c57e91b7c37b79be858f4ef1c0df1a259897e", - "url": "https://files.pythonhosted.org/packages/16/64/bd815f5c610b3facc204f26ba94e87a9eb49b0d83de3d5fc1eee2402d91b/orjson-3.11.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + "hash": "e4ac5059baab4b3acbd99485de019ff8cda0fdf34b61fa74f7197a53db78bfe8", + "url": "https://files.pythonhosted.org/packages/1c/df/b49081766a75b6a37b3d33bdc0a39e492abab8441dd25e3e1998e7b83fcb/orjson-3.12.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl" }, { "algorithm": "sha256", - "hash": "9ef6fe90aadef185c7b128859f40beb24720b4ecea95379fc9000931179c3a49", - "url": "https://files.pythonhosted.org/packages/16/6d/11867a3ffa3a3608d84a4de51ef4dd0896d6b5cc9132fbe1daf593e677bc/orjson-3.11.9-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl" + "hash": "11edb4660a6680abee9788a3a9072208a2c96538cc1322bd79542065229d8e54", + "url": "https://files.pythonhosted.org/packages/29/98/758cf90fbeaaafb7f8141bfac75a432099959f3a2f5db93a412e876415d8/orjson-3.12.0-cp312-cp312-macosx_15_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "3ebca4179031ee716ed076ffadc29428e900512f6fccee8614c9983157fcf19c", - "url": "https://files.pythonhosted.org/packages/16/fa/9d54b07cb3f3b0bfd57841478e42d7a0ece4a9f49f9907eecf5a45461687/orjson-3.11.9-cp311-cp311-macosx_15_0_arm64.whl" + "hash": "2d3a9da945a4d96ae758fdaaca56742e6b73b6fd554c5d8876f252a6dad70b83", + "url": "https://files.pythonhosted.org/packages/32/b5/5b934d251f8651f7e41df180ad0c57a6e1cabe15c7bd331638413a50ebc9/orjson-3.12.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl" }, { "algorithm": "sha256", - "hash": "f01c4818b3fc9b0da8e096722a84318071eaa118df35f6ed2344da0e73a5444f", - "url": "https://files.pythonhosted.org/packages/1e/51/3fb9e65ae76ee97bd611869a503fa3fc0a6e81dd8b737cf3003f682df7ff/orjson-3.11.9-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl" + "hash": "8e29957429c35bbb5a185a119c523aa2428b7bbf1a293724c7b9375ed8f892a3", + "url": "https://files.pythonhosted.org/packages/48/d4/58ea28eeef95c2a27358ed927380a621162cf20bd740bbccf9c3f09a200a/orjson-3.12.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl" }, { "algorithm": "sha256", - "hash": "53b50b0e14084b8f7e29c5ce84c5af0f1160169b30d8a6914231d97d2fe297d4", - "url": "https://files.pythonhosted.org/packages/1f/7d/30e844b3dac3f74aed66b1f984daf9db3c98c0328c03d965a9e8dc06449e/orjson-3.11.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "103b5db66aa53c1f9e88c2524be4f383e831ba7dfd5f9f5af6336a177c622f11", + "url": "https://files.pythonhosted.org/packages/4c/a6/22e863bbbe8917aa292e33e0db597000f9a07eb5e6f52efed623fa16bae1/orjson-3.12.0-cp310-cp310-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "e5c9b8f28e726e97d97696c826bc7bea5d71cecd63576dba92924a32c1961291", - "url": "https://files.pythonhosted.org/packages/24/75/05912954c8b288f34fcf5cd4b9b071cb4f6e77b9961e175e56ebb258089f/orjson-3.11.9-cp312-cp312-macosx_15_0_arm64.whl" + "hash": "bf44e374aadde77b1f6109f1030be51433eb61984379852766b6f4e187db7b1e", + "url": "https://files.pythonhosted.org/packages/50/22/0644b87c73f13e0092df8f35a1fe280d991e5e90072087411e0dd7e44e0c/orjson-3.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "71f3db16e69b667b132e0f305a833d5497da302d801508cbb051ed9a9819da47", - "url": "https://files.pythonhosted.org/packages/2a/56/d54152b67b63a0b3e556cfc549d6ce84f74d7f425ddeadc6c8a74d913da7/orjson-3.11.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "bd57d79aefa3f84eec851d6de7a366795b9345cfaf17f82b4820430a7a5fa241", + "url": "https://files.pythonhosted.org/packages/50/a0/ceb5008914a65e9a19a46a09d94bc67a74d120209fdfa772750023ceb377/orjson-3.12.0-cp310-cp310-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c2d3dc759490128c5c1711a53eeaa8ee1d437fd0038ffd2b6008abf46db3f882", - "url": "https://files.pythonhosted.org/packages/31/6a/6cf69385a58208024fcb8c014e2141b8ce838aba6492b589f8acfff97fab/orjson-3.11.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl" + "hash": "7c2ad193c8004254f34b499f3bd2c80f043d10754aff2b38f93da574f4883f98", + "url": "https://files.pythonhosted.org/packages/58/ab/d9221d4a2b085b073fcddc91728d490f20b9cf010c62c2f42371ab997695/orjson-3.12.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.whl" }, { "algorithm": "sha256", - "hash": "cde1a448023ba7d5bb4c01c5afb48894380b5e4956e0627266526587ef4e535f", - "url": "https://files.pythonhosted.org/packages/3a/dc/7446c538590d55f455647e5f3c61fc33f7108714e7afcffa6a2a033f8350/orjson-3.11.9-cp312-cp312-musllinux_1_2_i686.whl" + "hash": "9caf3d09f47c3c70c4451ada20ef9bc4a4cdffa26f49862cf0a253b329aae2d5", + "url": "https://files.pythonhosted.org/packages/5b/e1/15169e9d22b59a406264f99d6db387c0b0b12b6357a8a0169917c2a713eb/orjson-3.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "147302878da387104b66bb4a8b0227d1d487e976ce41a8501916161072ed87b1", - "url": "https://files.pythonhosted.org/packages/49/bd/360686f39348aa88827cb6fbf7dc606fd41c831a35235e1abf1db8e3a9e6/orjson-3.11.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "a94f0f0c6fcbb2b5bd9734c57a489c7584a732bbdf04a39e8c83b861e9d03e92", + "url": "https://files.pythonhosted.org/packages/75/1a/a7075a8e8b0d3f5097d17ac3099017104b6b7b42012041147995d5b2da05/orjson-3.12.0-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl" }, { "algorithm": "sha256", - "hash": "4da3c38a2083ca4aaf9c2a36776cce3e9328e6647b10d118948f3cfb4913ffe4", - "url": "https://files.pythonhosted.org/packages/4e/61/73d49333bba660a075daccca10970dc6409ce1cf42ae4046646a19468aad/orjson-3.11.9-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" + "hash": "e9683ee9ea0659da64f36574ef675b8a86330c34c19ea75db1fb93c3ff99e0ef", + "url": "https://files.pythonhosted.org/packages/75/20/930824c07685c22af23f26818ed3853b0270488a412b6ab757904b7f787b/orjson-3.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "a8f5f8bc7ce7d59f08d9f99fa510c06496164a24cb5f3d34537dbd9ca30132e2", - "url": "https://files.pythonhosted.org/packages/50/c7/375e83a76851b73b2e39f3bcf0e5a19e2b89bad13e5bca97d0b293d27f24/orjson-3.11.9-cp312-cp312-musllinux_1_2_aarch64.whl" + "hash": "53c0c474a9d9aff9aebfc0c88de1f28f843d940e6e3a80729abdf6a20274356f", + "url": "https://files.pythonhosted.org/packages/80/3d/75c5ac5a69161f44492a68fbdde66f4cc4ce48cd5e1fb05918e46f0c8848/orjson-3.12.0-cp312-cp312-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "c5d001196b89fa9cf0a4ab79766cd835b991a166e4b621ba95089edc50c429ff", - "url": "https://files.pythonhosted.org/packages/5f/f1/ff2f19ed0225f9680fafa42febca3570dd59444ebf190980738d376214c2/orjson-3.11.9-cp311-cp311-musllinux_1_2_armv7l.whl" + "hash": "1192a7021b6d071aaf909864f6e924d6a2675ca360485b972b8401749311750b", + "url": "https://files.pythonhosted.org/packages/8c/57/80b986ebfecd9c6a177ddf1c2319717f0cd8feffb2b78946595a18a2fc88/orjson-3.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "ace6c58523302d3b97b6ac5c38a5298a54b473762b6be82726b4265c41029f92", - "url": "https://files.pythonhosted.org/packages/64/cb/509c2e816fe4df641d93dc92f6a89adc8df3ada8ebdee2bd44aba3264c3c/orjson-3.11.9-cp310-cp310-musllinux_1_2_i686.whl" + "hash": "b9dca132b1fda5565088e65a6b6e742285e0aeceb6fae549fa8863e16c7d3998", + "url": "https://files.pythonhosted.org/packages/a4/3a/763dbd426290d044ec3e615a05e70adb6d8b6f95bf17dc355c0081a5e8b6/orjson-3.12.0-cp311-cp311-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f", - "url": "https://files.pythonhosted.org/packages/7e/0c/964746fcafbd16f8ff53219ad9f6b412b34f345c75f384ad434ceaadb538/orjson-3.11.9.tar.gz" + "hash": "aa3e43a6846e91d7bde3d5a9c66090fcd8744f569a9b6cffc5e1ca38f6a461c0", + "url": "https://files.pythonhosted.org/packages/be/4a/295da39c651c2faac8bd351a2a346f0fdedd9d50b847ee9dfc27d2207ef6/orjson-3.12.0-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl" }, { "algorithm": "sha256", - "hash": "4d7fde5501b944f83b3e665e1b31343ff6e154b15560a16b7130ea1e594a4206", - "url": "https://files.pythonhosted.org/packages/7f/7c/49d5d82a3d3097f641f094f552131f1e2723b0b8cb0fa2874ab65ecfffa6/orjson-3.11.9-cp312-cp312-musllinux_1_2_armv7l.whl" + "hash": "92ffc09e07233a6ab6d4e067f7841edcbcc134cb4812155cf171ea5255a421d7", + "url": "https://files.pythonhosted.org/packages/cd/d2/37efb5b12a176ce3ced29f4144f20da57d02757f78ce549637dc1b4e1fc8/orjson-3.12.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl" }, { "algorithm": "sha256", - "hash": "115ab5f5f4a0f203cc2a5f0fb09aee503a3f771aa08392949ab5ca230c4fbdbd", - "url": "https://files.pythonhosted.org/packages/80/01/be33fbff646e22f93398429ea645f20d2097aea1a6cdc1e6628e70125f83/orjson-3.11.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "747843254519dd43b93eee3153a19e5a509334320c4d2f823ec879232db5c796", + "url": "https://files.pythonhosted.org/packages/cf/35/819eeb4fa8ee676d38fdbb8213a76fd496f7dbbfdfafa89d34e02b22dfac/orjson-3.12.0-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl" }, { "algorithm": "sha256", - "hash": "48ee05097750de0ff69ed5b7bbcf0732182fd57a24043dcc2a1da780a5ead3a5", - "url": "https://files.pythonhosted.org/packages/88/b1/6ceafc2eefd0a553e3be77ce6c49d107e772485d9568629376171c50e634/orjson-3.11.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" - }, - { - "algorithm": "sha256", - "hash": "16969c9d369c98eb084889c6e4d2d39b77c7eb38ceccf8da2a9fff62ae908980", - "url": "https://files.pythonhosted.org/packages/9b/61/863bddf0da6e9e586765414debd54b4e58db05f560902b6d00658cb88636/orjson-3.11.9-cp311-cp311-musllinux_1_2_i686.whl" - }, - { - "algorithm": "sha256", - "hash": "277fefe9d76ee17eb14debf399e3533d4d63b5f677a4d3719eb763536af1f4bd", - "url": "https://files.pythonhosted.org/packages/9e/85/0ef63bcf1337f44031ce9b91b1919563f62a37527b3ea4368bb15a22e5d7/orjson-3.11.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl" - }, - { - "algorithm": "sha256", - "hash": "26a473dbb4162108b27901492546f83c76fdcea3d0eadff00ae7a07e18dcce09", - "url": "https://files.pythonhosted.org/packages/ab/86/1c3a47df3bc8191ea9ac51603bbb872a95167a364320c269f2557911f406/orjson-3.11.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" - }, - { - "algorithm": "sha256", - "hash": "63e0efbc991250c0b3143488fa57d95affcabbfc63c99c48d625dd37779aafe2", - "url": "https://files.pythonhosted.org/packages/b6/8a/4081492586d75b073d60c5271a8d0f05a0955cabf1e34c8473f6fcd84235/orjson-3.11.9-cp311-cp311-musllinux_1_2_x86_64.whl" - }, - { - "algorithm": "sha256", - "hash": "34fd2317602587321faab75ab76c623a0117e80841a6413654f04e47f339a8fb", - "url": "https://files.pythonhosted.org/packages/c7/35/e744fd36c79b339d27beb06068b5a08a8882ef5418804d0ce545a31f718d/orjson-3.11.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl" - }, - { - "algorithm": "sha256", - "hash": "011382e2a60fda9d46f1cdee31068cfc52ffe952b587d683ec0463002802a0f4", - "url": "https://files.pythonhosted.org/packages/d7/cf/b33b5f3e695ae7d63feef9d915c37cc3b8f465493dcd4f8e0b4c697a2366/orjson-3.11.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" - }, - { - "algorithm": "sha256", - "hash": "97d0d932803c1b164fde11cb542a9efcb1e0f63b184537cca65887147906ff48", - "url": "https://files.pythonhosted.org/packages/db/b5/3ceae56d2e4962979eedb023ba6a46a4bb65f333960379be0ca470686220/orjson-3.11.9-cp310-cp310-musllinux_1_2_x86_64.whl" - }, - { - "algorithm": "sha256", - "hash": "d8ea516b3726d190e1b4297e6f4e7a8650347ae053868a18163b4dd3641d1fff", - "url": "https://files.pythonhosted.org/packages/e8/f8/0b1bd3e8f2efcdd376af5c8cfd79eaf13f018080c0089c80ebd724e3c7fb/orjson-3.11.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" - }, - { - "algorithm": "sha256", - "hash": "a6082706765a95a6680d812e1daf1c0cfe8adec7831b3ff3b625693f3b461b1c", - "url": "https://files.pythonhosted.org/packages/ea/76/f11311285324a40aab1e3031385c50b635a7cd0734fdaf60c7e89a696f60/orjson-3.11.9-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" - }, - { - "algorithm": "sha256", - "hash": "380cdce7ba24989af81d0a7013d0aaec5d0e2a21734c0e2681b1bc4f141957fe", - "url": "https://files.pythonhosted.org/packages/f3/59/dab79f61044c529d2c81aecdc589b1f833a1c8dec11ba3b1c2498a02ca7e/orjson-3.11.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl" + "hash": "dce0166feb0a737ab84f598c9a338cbc0b764a036617aa686194f53c7eba0c3e", + "url": "https://files.pythonhosted.org/packages/e2/f4/1e82aa2efc9916422d804697876ce433c907a1abd7c7e5c6d3d48565e5f9/orjson-3.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" } ], "project_name": "orjson", "requires_dists": [], "requires_python": ">=3.10", - "version": "3.11.9" + "version": "3.12.0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "491767e81c1bb11a54fb68d1a24119bdeede593a2beccca5bc09bfed36fdb35c", + "hash": "b9feb1769b48102061fe4fc59b2f5ad600bc2ac0b55cf12ef5fe49464ac0d230", "url": "git+https://github.com/StackStorm/orquesta.git" } ], @@ -3304,19 +3369,19 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", - "url": "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl" + "hash": "d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", + "url": "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", - "url": "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz" + "hash": "94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", + "url": "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz" } ], "project_name": "packaging", "requires_dists": [], - "requires_python": ">=3.8", - "version": "26.2" + "requires_python": ">=3.9", + "version": "26.3" }, { "artifacts": [ @@ -3390,13 +3455,13 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "2daae7bd422a0fc4f4879fd48c9a1932ed74a0bc7172e1e5f9bde63a101ed074", - "url": "https://files.pythonhosted.org/packages/7f/7b/a09c0d378ee8604220902c58103104b2304ef99744c4b7fc45312d280484/pika-1.4.1-py3-none-any.whl" + "hash": "48de960c97a93b55db06b8be4c53eb977c9c8a2754c57cdae9097abcbd70ce04", + "url": "https://files.pythonhosted.org/packages/7c/f3/921170b78779ac3f8b405cb28fce88acca1562ac114072cbf0a115e19bb9/pika-1.4.4-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "e851f3e4992adfbf8eb64e9b86d94e3382f92ba0200055abedbb29676b8e713b", - "url": "https://files.pythonhosted.org/packages/f7/09/8c42dd00c4b3e09ebd174455d30823c261eefef9d2b7e94ae9ca779704c1/pika-1.4.1.tar.gz" + "hash": "8cfc8b33a5cb16e733bd60cffca9732c0d1d761ecd80a89f34ed7df2cd38d6d6", + "url": "https://files.pythonhosted.org/packages/7f/26/54e0b98a7f60b474cb0a6c05ecf048d4bc8c866e10ab1c82bc83865e7421/pika-1.4.4.tar.gz" } ], "project_name": "pika", @@ -3406,43 +3471,43 @@ "twisted; extra == \"twisted\"" ], "requires_python": ">=3.7", - "version": "1.4.1" + "version": "1.4.4" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "382ff9f685ee3bc25864f820aa50505825f10f5458ffff07e30a6d96e5715cab", - "url": "https://files.pythonhosted.org/packages/5d/95/6b5cb3461ea5673ba0995989746db58eb18b91b54dbf331e72f569540946/pip-26.1.2-py3-none-any.whl" + "hash": "71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e", + "url": "https://files.pythonhosted.org/packages/f3/6e/1736e5b4ae2b778ef2f81c47d797de9f891d4d8acb047a24ca37a60294dd/pip-26.2.1-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "f49cd134c61cf2fd75e0ce2676db03e4054504a5a4986d00f8299ae632dc4605", - "url": "https://files.pythonhosted.org/packages/01/91/47e7d486260f618783899587af63ccf7980fb60245c3e63dd4571c6b57ad/pip-26.1.2.tar.gz" + "hash": "f6ad667e89a1fe78046c8f13232b247200f5258d7828f3f7883d660878e0813f", + "url": "https://files.pythonhosted.org/packages/ae/15/4500e320e6b101ec3b719ae85b697d9940b6cda672bc555bd6016fc60c6f/pip-26.2.1.tar.gz" } ], "project_name": "pip", "requires_dists": [], "requires_python": ">=3.10", - "version": "26.1.2" + "version": "26.2.1" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", - "url": "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl" + "hash": "89f8d42695853b89c7170bd49bc3dc593f98a71e695ede88e06a3b247bc4563b", + "url": "https://files.pythonhosted.org/packages/c7/12/6f3fcd5067a9cbf4f8664b32957973498da8b083455203c8d9cab83a725c/platformdirs-4.11.5-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", - "url": "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz" + "hash": "e8b31f4f8bcbbedef91a6b57a706255e4f148d2a4e01648382a0a47342539173", + "url": "https://files.pythonhosted.org/packages/ea/06/cf1564dcc2e2261c8c8c6c05628dc8b418943bdae2a4e58640ceb2f770fa/platformdirs-4.11.5.tar.gz" } ], "project_name": "platformdirs", "requires_dists": [], "requires_python": ">=3.10", - "version": "4.10.0" + "version": "4.11.5" }, { "artifacts": [ @@ -3508,35 +3573,30 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "d3c362036d625b12aeee495621cb1555fd50b2af3632af3d825176bfb50e073b", - "url": "https://files.pythonhosted.org/packages/a9/a8/fc509e514c708f43102542cdcbc2f42dc49f7a159f90f56d072371629731/prance-25.4.8.0-py3-none-any.whl" + "hash": "b98b26cd968f306e6d4ced3eddb78ef4cceb5311ab3f76d76573fca4a0e01837", + "url": "https://files.pythonhosted.org/packages/05/d3/f966d7fddc2ed0f3c9e196df4f7841020629252915d0eb1f44e47e268b50/prance-26.7.19.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "2f72d2983d0474b6f53fd604eb21690c1ebdb00d79a6331b7ec95fb4f25a1f65", - "url": "https://files.pythonhosted.org/packages/ae/5c/afa384b91354f0dbc194dfbea89bbd3e07dbe47d933a0a2c4fb989fc63af/prance-25.4.8.0.tar.gz" + "hash": "18c52bc9ace4de2b58be1d810543d675a8640f06c2ea5d06427f12f1e612be8d", + "url": "https://files.pythonhosted.org/packages/4a/a8/68f8193e875eb9ab2c4811048e6bdfebf76c33cfdf274e946fd962530f65/prance-26.7.19.0.tar.gz" } ], "project_name": "prance", "requires_dists": [ "PyICU~=2.14; extra == \"icu\"", - "bumpversion>=0.6.0; extra == \"dev\"", "chardet>=5.2", - "click>=8.1.8; extra == \"cli\"", + "click>=8.3.0; extra == \"cli\"", "flex~=6.14.1; extra == \"flex\"", "openapi-spec-validator~=0.7.1; extra == \"osv\"", - "packaging>=24.2", - "pytest-cov>=6.0; extra == \"dev\"", - "pytest>=8.3.5; extra == \"dev\"", - "requests>=2.32.3", - "ruamel.yaml>=0.18.10", - "sphinx>=8.1.3; extra == \"dev\"", - "swagger-spec-validator~=3.0.4; extra == \"ssv\"", - "towncrier>=24.8; extra == \"dev\"", - "tox>=4.23.2; extra == \"dev\"" + "orjson>=3.9; extra == \"fast\"", + "packaging>=25.0", + "requests>=2.32.5", + "ruamel.yaml>=0.18.16", + "swagger-spec-validator~=3.0.4; extra == \"ssv\"" ], "requires_python": ">=3.10", - "version": "25.4.8.0" + "version": "26.7.19.0" }, { "artifacts": [ @@ -3565,21 +3625,21 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", - "url": "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl" + "hash": "01c0891d7f9237d5e339f7d3e42cdae80b7534abb1c7c0e3352efba6231492f2", + "url": "https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", - "url": "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz" + "hash": "9ec8a0ad96d5c56148b3f914aa79c1564c3fde5d2e6b876e7bc327e353cf8fa6", + "url": "https://files.pythonhosted.org/packages/7d/ea/39b988c938f75cb75d7045b5c69f8bfed47ee2152c8837fb403de29d6fb8/prompt_toolkit-3.0.53.tar.gz" } ], "project_name": "prompt-toolkit", "requires_dists": [ - "wcwidth" + "wcwidth>=0.1.4" ], - "requires_python": ">=3.8", - "version": "3.0.52" + "requires_python": ">=3.10", + "version": "3.0.53" }, { "artifacts": [ @@ -3759,248 +3819,248 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", - "url": "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl" + "hash": "346a034f080da3755d8e9cb5e00e8b07de1d39e4f6e2c87d8ab7cafa0b269a73", + "url": "https://files.pythonhosted.org/packages/eb/47/c95ffc2009878c7aac0c5e08528022dcb885933252a88b5f170058014464/pydantic-2.13.5-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", - "url": "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz" + "hash": "51a9c5f7b2f8e636f04c6cada605d9b6a3bf1348fdf945a3d8869b19bba0ee08", + "url": "https://files.pythonhosted.org/packages/53/ef/fc4f868f4e2cee79f863883abffceff107875f569b848507319842d2a681/pydantic-2.13.5.tar.gz" } ], "project_name": "pydantic", "requires_dists": [ "annotated-types>=0.6.0", "email-validator>=2.0.0; extra == \"email\"", - "pydantic-core==2.46.4", + "pydantic-core==2.46.5", "typing-extensions>=4.14.1", "typing-inspection>=0.4.2", "tzdata; (python_version >= \"3.9\" and platform_system == \"Windows\") and extra == \"timezone\"" ], "requires_python": ">=3.9", - "version": "2.13.4" + "version": "2.13.5" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", - "url": "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl" + "hash": "b6acfb46a814762367fb7ba0828b0a17d441b92ce249a0e007474c9072662dda", + "url": "https://files.pythonhosted.org/packages/02/8e/b41c84c913f29973a268e6c2b5bbf13c95adb9956c126d10da11ba3b2bef/pydantic_core-2.46.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", - "url": "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl" + "hash": "4fdc8b93a41521988916eeaa271173fcca7fa0803d62f87675aac8dcec1c8e29", + "url": "https://files.pythonhosted.org/packages/09/f0/2a8ce3849e299d44e2d2c196b6082643a3235565a735cb51db7a6261f614/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", - "url": "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl" + "hash": "d625a186a65201c23a9e3b8ed9c47e90a026e03256608cc91851c6709096844f", + "url": "https://files.pythonhosted.org/packages/0c/18/b70da8300e292df4099684ea11b1958043580d2f50d2dc8bf7e542bdd84a/pydantic_core-2.46.5-cp311-cp311-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", - "url": "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl" + "hash": "356c8368cbc321050b169595683a2e1d63413b1e0e2868b330af9fc14c616d3f", + "url": "https://files.pythonhosted.org/packages/13/5c/79fc00cb8f651d6061991de8d7cedf1c78c73cbd4862c42ef418f03b8bfa/pydantic_core-2.46.5-cp310-cp310-musllinux_1_1_armv7l.whl" }, { "algorithm": "sha256", - "hash": "962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", - "url": "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl" + "hash": "2bc9419666990c06d7397831f2126a1ecc3594aaa3ff7de5bf2d066802f4e07b", + "url": "https://files.pythonhosted.org/packages/1e/ee/ca3b7b3a4b3769ffe9ce9432a7c9be755de9593a46d3b0d54d0409323e44/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_armv7l.whl" }, { "algorithm": "sha256", - "hash": "56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", - "url": "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl" + "hash": "413a717a410d0c817ef5b786a059415550b3794e1d0c2abffd9efb93a3d9f7b4", + "url": "https://files.pythonhosted.org/packages/1f/93/fd89e9ad49b1805ca94d24ce1088b7d305f05c35ffafcedb9819d03588a0/pydantic_core-2.46.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", - "url": "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl" + "hash": "ab4b66edffb32d9e951efb3814bd104b8367a7501b81b955cacb5726d897389f", + "url": "https://files.pythonhosted.org/packages/20/21/22102e9950b3049526d20e811b95396508377d87651edd2b80d2b3d28659/pydantic_core-2.46.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", - "url": "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "cd5214352ae68f3b5e9af7768bdc5253695ee069675db3480518420b3be881f2", + "url": "https://files.pythonhosted.org/packages/20/32/7d6ca365fadba186a0c8f85de1a701663bce81efd309d9479be58687622f/pydantic_core-2.46.5-cp311-cp311-musllinux_1_1_aarch64.whl" }, { "algorithm": "sha256", - "hash": "18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", - "url": "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl" + "hash": "53feb344243bb9510a9dec7bf3cf1b64d88a98af5dc7872a5160465f8b198c8e", + "url": "https://files.pythonhosted.org/packages/24/98/7b76b1ad10a19a617a52aaa1d80e159115af939b095e86f8e756fd52e0df/pydantic_core-2.46.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", - "url": "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "fc5d783bd4a2387e97b8a2d5ec781cfb92b3d893bf82370548e99db5915935d3", + "url": "https://files.pythonhosted.org/packages/30/c8/47c79b756f12f85e8b0fbdb2b495f6b6eb32e6c98a4beae7a570a0b7c63c/pydantic_core-2.46.5-cp310-cp310-musllinux_1_1_aarch64.whl" }, { "algorithm": "sha256", - "hash": "f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", - "url": "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" + "hash": "850a08d167dde16db8702c274f320c7be9d7da6f6dff2b58b18f9e815bd94f5b", + "url": "https://files.pythonhosted.org/packages/34/a1/c8e6b66f499f510752c07a092dfe27621f9c255635e59d38704b5681c35a/pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", - "url": "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl" + "hash": "6d30e1a4f138b8951063e9a394752a9179b51da288ffa507b1e659222f4c1793", + "url": "https://files.pythonhosted.org/packages/3d/93/ce93aa030ab6bac4683ba8861e7baad89dd24b02e66b8801a0e4f6a00311/pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", - "url": "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" + "hash": "4dedce55295becb61921e386b99d4f2706045306e7fa52249a33004c837379fb", + "url": "https://files.pythonhosted.org/packages/4a/77/ca2f8e997d9bfdb32205297aff38f210f398822d895b1af1b59fd9df9c13/pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", - "url": "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl" + "hash": "aca6c767f552b21b10f774aeac128e828eafb796adfa1b666a18bf6321453c3a", + "url": "https://files.pythonhosted.org/packages/4a/ea/f86073830e35d508cc8ddf9c3d9e6e6840fcb88d34bf726b0b4710186f27/pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", - "url": "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "3a3e26b6a8274211bddee2d0e4d0d42778f17a34510f49d2ec44b58abfc41736", + "url": "https://files.pythonhosted.org/packages/4a/f7/1ab28093c09032ddce7c92c7a55d503b6ecd70f42c32492946c1cb5477b1/pydantic_core-2.46.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", - "url": "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "c3471e5c4a949c26ec00a77f01df59096aa9495877de76fd60a980f8ee6be461", + "url": "https://files.pythonhosted.org/packages/5c/fa/605e2b127ee30dbf4b1da9da4843587cf2b2d16486c241cc7a5be2d2c1bd/pydantic_core-2.46.5-cp310-cp310-manylinux_2_31_riscv64.whl" }, { "algorithm": "sha256", - "hash": "9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", - "url": "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl" + "hash": "1e449def1945a462c464331254e5a44fca7c3b4f9aedf59ec2f50f8066dd8e25", + "url": "https://files.pythonhosted.org/packages/6f/45/8e59dab6acf8d35f02f0a958980074f31038968bdb2c983fcae9d1efee03/pydantic_core-2.46.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", - "url": "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl" + "hash": "efd62a42486f1bda5d24cb4f63d15a3c7768375fe83d36f9417b4ad7a2fb20b3", + "url": "https://files.pythonhosted.org/packages/70/0a/644597d84ab400e50609c192120b85c9681c22d3a20461b9060a79be0a7a/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_aarch64.whl" }, { "algorithm": "sha256", - "hash": "17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", - "url": "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl" + "hash": "657b40d6240c0a7b6a64b30f22d1e3aa631c7e846c621b0c0f6d1d75e2e15ea6", + "url": "https://files.pythonhosted.org/packages/74/6b/8f79692844269427abb3e4dd9e68edfcbe65ae25527d99183214de716c59/pydantic_core-2.46.5-cp310-cp310-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", - "url": "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl" + "hash": "d925f3d9afd05a8c0fb3a1031463a8d59ebe5e2afad297e29c78be19e13b4e62", + "url": "https://files.pythonhosted.org/packages/7b/a0/9ff22b797724262da14427abaed4dd1d864a139693fc5e7809114376a716/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", - "url": "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl" + "hash": "5f93c5fe914d75fbec9a49209b00da5f08e9e467d69da2b1510c81940cfd10be", + "url": "https://files.pythonhosted.org/packages/7d/1d/02eb35761c51f2f7b1b042d6ab4cda6600f0c8c88a2243b3f734376201e5/pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", - "url": "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "b9fe6fb92520e3fd61f2e49000b6911b188824f089b75973ea06d6267f0b476d", + "url": "https://files.pythonhosted.org/packages/82/3f/76358795aa7a8c6d4f36e2cb828ad1c90ee118e1393a9281664f5aade9d4/pydantic_core-2.46.5-cp312-cp312-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", - "url": "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl" + "hash": "b98134087d9de723658d17a42c7d0da8d6e2ef08015dee7dc93889047315f5e4", + "url": "https://files.pythonhosted.org/packages/87/46/ac0dc8bdd9e6048183a14eb127764e7ad9240021c17513074a4711b0e31e/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", - "url": "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "8feeac04b5794e513e710af2f9c87d49f31a6dc47967bb264a1fed61a8989bec", + "url": "https://files.pythonhosted.org/packages/8a/f9/548a5bb9d4ba8cd26e26daf48052236f6b38bb61e7b7241fbc3c995719eb/pydantic_core-2.46.5-cp311-cp311-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", - "url": "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + "hash": "c7ea57fc63aa7da93a1bd2d644e6577befae10c52c4e36377635eea1056a74f5", + "url": "https://files.pythonhosted.org/packages/92/b1/e781d357ebe09fc929f995700f1b3503e8897f1cece183ecb1300d4d67e9/pydantic_core-2.46.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", - "url": "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl" + "hash": "9f47b8a949e60f027f0aa0a6f6c7b7e9c55cbf4380d10b344e282fa4e7ab1e1b", + "url": "https://files.pythonhosted.org/packages/a0/53/bd12e1a9255df4edee00353778e2614b5346265d51e1567ab72153e803a2/pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", - "url": "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz" + "hash": "a1dee1b804ff4d11c663636cf15d2ea47e9f79cd56c033fb1cbf08924842a48f", + "url": "https://files.pythonhosted.org/packages/a2/b6/81d2d19ea0be2c03664381b59f65fa72fc7969decedae00bc2c4ad835708/pydantic_core-2.46.5-cp311-cp311-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", - "url": "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl" + "hash": "a2468d93d181667a7abd66e1b64bb9f76f361b0fef8faddf687456453576f5ee", + "url": "https://files.pythonhosted.org/packages/ab/9a/095d557bb492c90cd8a70a6dd048bf793d433d03d86c81c11e912e4cd049/pydantic_core-2.46.5-cp311-cp311-manylinux_2_31_riscv64.whl" }, { "algorithm": "sha256", - "hash": "b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", - "url": "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl" + "hash": "10416c15b8839ecc4ef4d0885da76da6fd0f67333a0eb8aff6d93c4b8f2910fc", + "url": "https://files.pythonhosted.org/packages/af/f9/8a06bea35ef8daf588f707784c973a7046e0034c8d8cfb08828eeffb8b75/pydantic_core-2.46.5.tar.gz" }, { "algorithm": "sha256", - "hash": "d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", - "url": "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl" + "hash": "eb7d8d0e5886a89a55d2eef490e272fa965a9d57c6b29a5b5088a7997ec2cad1", + "url": "https://files.pythonhosted.org/packages/b4/72/dd1a29853cf6d22a1ebd9e3baf0239cbc57d2d16caff36a89e38eb9b1db3/pydantic_core-2.46.5-cp310-cp310-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", - "url": "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl" + "hash": "701b2e04b560eeb4bddf7a25ab8ca476176e34fdbd9a0e18196f0d12d4685f0b", + "url": "https://files.pythonhosted.org/packages/bb/d7/fc36240d7791ce90939e51608568c33bfdae26202016f9770c229a487d86/pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", - "url": "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl" + "hash": "ecb42011e12ee19cafbc312887cbf3546959fe02fbad44f272d4be5baa997615", + "url": "https://files.pythonhosted.org/packages/bd/d0/c787604c71c2bdcda1a5656942fc822cd0f9cd879b9484bb84fc42172703/pydantic_core-2.46.5-cp310-cp310-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", - "url": "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl" + "hash": "816ff0a6550ffc06c098ccd2e0698600f9aa7da192a79eaa6f9af504a35db869", + "url": "https://files.pythonhosted.org/packages/c0/02/7f6156ffc926857f1c37c07d9a388682865a81830ab6a1b637082c25e399/pydantic_core-2.46.5-cp312-cp312-manylinux_2_31_riscv64.whl" }, { "algorithm": "sha256", - "hash": "395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", - "url": "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "0fc5be0abd4a407e200d844b404e33639a554e7bd0d448e7b9ae181be4789ac2", + "url": "https://files.pythonhosted.org/packages/c0/a4/eb9409ec0736e50aa70a412f16c204ed149516846912f7e6724d4c73ee53/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", - "url": "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl" + "hash": "2d330aaba8621b1edcec8ae2c4050f63b84ccf6d98723a8f212e9684713abf0e", + "url": "https://files.pythonhosted.org/packages/c2/d7/ad493864a7fb21c0c4df98f965e2db430cb25a9d7369b5778d5016c09fd9/pydantic_core-2.46.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl" }, { "algorithm": "sha256", - "hash": "905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", - "url": "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + "hash": "e652ab17569c94bff5475520f907b7148b8c24036a8ebbe5cf7cf7493d28579a", + "url": "https://files.pythonhosted.org/packages/c4/c2/339de5bef7be36301a2231eaa52e62163742c2281f11b5f4892bc79785cd/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", - "url": "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl" + "hash": "18a09e1e1011b462f2e32774f25859ef1223d5c2b0546a633cf56654710721e0", + "url": "https://files.pythonhosted.org/packages/ce/52/39fa1f451486019524ca685020390e7ca351832fd874530ba30c8628e6dc/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", - "url": "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl" + "hash": "49776eab08766a08dfff7012f8b422dcd7e25e43b316eedf0477c24fcfa84b7c", + "url": "https://files.pythonhosted.org/packages/cf/bc/3fa2d76b83162820a17da7f645b28d1cba99fc8e1e5fc6517067ec450fa1/pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", - "url": "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl" + "hash": "a445486499897b88a7d6c310c88ed64dd37b1b59bfd7ae9107490bbb362f47d6", + "url": "https://files.pythonhosted.org/packages/d5/a5/e1d4dc5180dd887a9522efc1f8716b8692b7606b1d3273d7862eaf66be44/pydantic_core-2.46.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl" }, { "algorithm": "sha256", - "hash": "9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", - "url": "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl" + "hash": "200aa3dc9f8d54f0754f43247c0bad0999fdcfbfd2488384dd44f37279271fe6", + "url": "https://files.pythonhosted.org/packages/d7/41/f7f312751ebc6d6767da91964a9c7954c18e226a1720ab234e3dfb9d6c17/pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", - "url": "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl" + "hash": "337639ba62a11acde6ef3aeb08c8ea755f8ef1fe5e513356c0f36a2b0d7568b0", + "url": "https://files.pythonhosted.org/packages/d8/18/87aefa427d191e6d3ab1447f1efc1cdcac86af1069239b133e8a0fd7f7c9/pydantic_core-2.46.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", - "url": "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" + "hash": "a39ac25a9a2fa4072efdb429833c4a4c8009a51ff9eea3eeae131713cd27991e", + "url": "https://files.pythonhosted.org/packages/db/50/26b091836076ce4cb2fac264186936acc069e0595772cfd02a563bc4761a/pydantic_core-2.46.5-cp312-cp312-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", - "url": "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + "hash": "4f8507560a9284e1370bb048ed4282012fbef4e8d109875b95e884d228552061", + "url": "https://files.pythonhosted.org/packages/e7/1a/0d590341b6ffa4b4aca83508e6b8db4761aaeacfc15a25ca3815876d4797/pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", - "url": "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl" + "hash": "9432f3598db432cb51c5b37fdbf29a60fcccc79e30d37a05022776a6bc4ab689", + "url": "https://files.pythonhosted.org/packages/f8/09/eb9a6aa57f22fd1541a9c0aa2a1f3aeef3ec65347d33e10a6da2f43e0ee9/pydantic_core-2.46.5-cp311-cp311-musllinux_1_1_armv7l.whl" } ], "project_name": "pydantic-core", @@ -4008,19 +4068,19 @@ "typing-extensions>=4.14.1" ], "requires_python": ">=3.9", - "version": "2.46.4" + "version": "2.46.5" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440", - "url": "https://files.pythonhosted.org/packages/77/c1/6e422f34e569cf8e18df68d1939c81c099d2b61e4f7d9621c8a77560799c/pydantic_settings-2.14.2-py3-none-any.whl" + "hash": "0ba092c291c94baceb5eff768aa0d56400a457585bc0175925a5a5510303da42", + "url": "https://files.pythonhosted.org/packages/30/a4/2bffa9f8e804325a09867f0e9d30795c80ea9f8d62560bd1b6ad6220eb2f/pydantic_settings-2.15.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f", - "url": "https://files.pythonhosted.org/packages/5c/b5/8f48e906c3e0205276e8bd8cb7512217a87b2685304d64be27cad5b3019f/pydantic_settings-2.14.2.tar.gz" + "hash": "694b793e84f766ba76a90ebdefc01d0a9a045dab0382bee70393da93712ad117", + "url": "https://files.pythonhosted.org/packages/68/ca/31c57507b13119d7d3cfa1576dad2911a4861e3be07b579395f4e9d393f9/pydantic_settings-2.15.0.tar.gz" } ], "project_name": "pydantic-settings", @@ -4033,11 +4093,10 @@ "python-dotenv>=0.21.0", "pyyaml>=6.0.1; extra == \"yaml\"", "tomli>=2.0.1; extra == \"toml\"", - "types-boto3[secretsmanager]; extra == \"aws-secrets-manager\"", "typing-inspection>=0.4.0" ], "requires_python": ">=3.10", - "version": "2.14.2" + "version": "2.15.0" }, { "artifacts": [ @@ -4097,13 +4156,13 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", - "url": "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl" + "hash": "2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", + "url": "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", - "url": "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz" + "hash": "610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", + "url": "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz" } ], "project_name": "pygments", @@ -4111,7 +4170,7 @@ "colorama>=0.4.6; extra == \"windows-terminal\"" ], "requires_python": ">=3.9", - "version": "2.20.0" + "version": "2.21.0" }, { "artifacts": [ @@ -4387,13 +4446,13 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "7237cb47985ccf5da512106ddb2731e4f9cefec00991f76c054488eb95fb1a2d", - "url": "https://files.pythonhosted.org/packages/dd/9f/4da6a1b9611af2397289b77d6fd08f5fe8f1f34dabc3bf85b0638d655e64/pyspnego-0.12.1-py3-none-any.whl" + "hash": "04e7c9260ced15197b3f42b891ffa30fa98c37bb09372c5c2bf927bcdfe30b53", + "url": "https://files.pythonhosted.org/packages/0e/8c/3d0154b8781a371ef1b791063da5a223187dd17738c5b188edc5e9657f9a/pyspnego-0.12.2-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "ff4fb6df38202a012ea2a0f43091ae9680878443f0ea61c9ea0e2e8152a4b810", - "url": "https://files.pythonhosted.org/packages/7d/84/58577bd1b14293650879de0579ec263a1d8350f1d6d227226cf776b5a6a6/pyspnego-0.12.1.tar.gz" + "hash": "448a491a9bf0e5fb957567fe46e6809fa836aa40dfcccebcc740bb64acb1be1c", + "url": "https://files.pythonhosted.org/packages/03/c4/790211e7a2180509dc29137c488c7b038ef89bdf83db0ce4da802f64f9d4/pyspnego-0.12.2.tar.gz" } ], "project_name": "pyspnego", @@ -4402,10 +4461,10 @@ "gssapi>=1.6.0; sys_platform != \"win32\" and extra == \"kerberos\"", "krb5>=0.3.0; sys_platform != \"win32\" and extra == \"kerberos\"", "ruamel.yaml; extra == \"yaml\"", - "sspilib>=0.3.0; sys_platform == \"win32\"" + "sspilib>=0.5.0; sys_platform == \"win32\"" ], "requires_python": ">=3.9", - "version": "0.12.1" + "version": "0.12.2" }, { "artifacts": [ @@ -4500,22 +4559,21 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "810958f66a91afb1a1e2ae83089d8dc1cd2437ac96b12963042fbb9fb4d16af0", - "url": "https://files.pythonhosted.org/packages/f4/af/9c0bda43e486a3c9bf1e0f876d0f241bc3f229d7d65d09331a0868db9629/pytest_forked-1.6.0-py3-none-any.whl" + "hash": "e9f3475fa0a42927f5e370d721de9c2d785616a06a4c506712d6cb8055e37c84", + "url": "https://files.pythonhosted.org/packages/18/f1/46d32fe4b9aae09fe397e768ff376d9b6bdbb4f11faaa727f163c3457b43/pytest_forked-1.7.5-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "4dafd46a9a600f65d822b8f605133ecf5b3e1941ebb3588e943b4e3eb71a5a3f", - "url": "https://files.pythonhosted.org/packages/8c/c9/93ad2ba2413057ee694884b88cf7467a46c50c438977720aeac26e73fdb7/pytest-forked-1.6.0.tar.gz" + "hash": "00f2bee51612f29b8e6b81eed2c3b2975e824c2693394f5bdaf7a1369078ba5f", + "url": "https://files.pythonhosted.org/packages/99/92/98bd460b998f9ec053acba2e3efbbca12a9a408ec8648bd55abd2df784f0/pytest_forked-1.7.5.tar.gz" } ], "project_name": "pytest-forked", "requires_dists": [ - "py", - "pytest>=3.10" + "pytest>=7" ], - "requires_python": ">=3.7", - "version": "1.6.0" + "requires_python": ">=3.10", + "version": "1.7.5" }, { "artifacts": [ @@ -4588,45 +4646,33 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "abebe9120b43453b68c908acfb1e72a19d1a959ed2cb620ad38fc57d08056dbe", - "url": "https://files.pythonhosted.org/packages/9d/ae/84bc0d2440c95772272bb6f4b3d09ccf08b2898fce89b3d4f969a9fc74e9/python_discovery-1.4.4-py3-none-any.whl" + "hash": "d4e244cf17b8b29819ed78003d55fbacf86eda23425b075454fff9271b79377a", + "url": "https://files.pythonhosted.org/packages/43/5e/21abf578182fb15006a57faf3711a1e659e29d600d19b6e557eae908c81d/python_discovery-1.6.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "5cad33982d412c1f3ffb8f9ca4ea292c9680bca3942451d30b69c37fce53a4a3", - "url": "https://files.pythonhosted.org/packages/4c/81/58c70036dffeccb7fe7d79d6260c69f7a28272bbd3909c29a01ea9422744/python_discovery-1.4.4.tar.gz" + "hash": "6393b4eae1be8b2182670635e7baff89ac21cb9f8e86fd1ff40c7b1144febb4c", + "url": "https://files.pythonhosted.org/packages/91/96/0f93e27c9f60a650838f2118159aa115fd5732c0716247917b7ba7ede665/python_discovery-1.6.0.tar.gz" } ], "project_name": "python-discovery", "requires_dists": [ - "covdefaults>=2.3; extra == \"testing\"", - "coverage>=7.5.4; extra == \"testing\"", - "filelock>=3.15.4", - "furo>=2025.12.19; extra == \"docs\"", - "platformdirs<5,>=4.3.6", - "pytest-mock>=3.14; extra == \"testing\"", - "pytest>=8.3.5; extra == \"testing\"", - "setuptools>=75.1; extra == \"testing\"", - "sphinx-autodoc-typehints>=3.6.3; extra == \"docs\"", - "sphinx>=9.1; extra == \"docs\"", - "sphinxcontrib-mermaid>=2; extra == \"docs\"", - "sphinxcontrib-towncrier>=0.4; extra == \"docs\"", - "towncrier>=25.8; extra == \"docs\"" + "filelock>=3.15.4" ], "requires_python": ">=3.8", - "version": "1.4.4" + "version": "1.6.0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", - "url": "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl" + "hash": "904552145e8bfed22162c09dab1c2b9b54fefa7b23ba780f4f26ca0316b0f0d9", + "url": "https://files.pythonhosted.org/packages/0d/17/c5c6b53ddc18f297992099b3d9ec16c855c0ccc83263a21fe4d1c625ec6c/python_dotenv-1.2.3-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", - "url": "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz" + "hash": "a20a594dabeaa385725aa239d5244871c143ecb356add8a20fcf23773a6c3a35", + "url": "https://files.pythonhosted.org/packages/6a/53/ed9d74092561d4b01a2ef1349d52cdbc135e526c245f366b089cfca6de49/python_dotenv-1.2.3.tar.gz" } ], "project_name": "python-dotenv", @@ -4634,44 +4680,25 @@ "click>=5.0; extra == \"cli\"" ], "requires_python": ">=3.10", - "version": "1.2.2" + "version": "1.2.3" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2", - "url": "https://files.pythonhosted.org/packages/27/be/0631a861af4d1c875f096c07d34e9a63639560a717130e7a87cbc82b7e3f/python_json_logger-4.1.0-py3-none-any.whl" + "hash": "158a52126fcd6869e09574d2b66272666f3dc8f468c62637ef9a1fa883719cb9", + "url": "https://files.pythonhosted.org/packages/dc/55/6467fde553886cb293e41538f3a8b4e4fd4688c6df242cf982162d8367fb/python_json_logger-4.2.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195", - "url": "https://files.pythonhosted.org/packages/f7/ff/3cc9165fd44106973cd7ac9facb674a65ed853494592541d339bdc9a30eb/python_json_logger-4.1.0.tar.gz" + "hash": "e371ebe22ec01e289850102091a2b1f6fc9e655c7f1f5f29073936756c290afa", + "url": "https://files.pythonhosted.org/packages/21/25/5473e46b179f8e8b4ad3aeeb36773d1701b7770eaf5e5bc2025c7303b598/python_json_logger-4.2.0.tar.gz" } ], "project_name": "python-json-logger", - "requires_dists": [ - "black; extra == \"dev\"", - "build; extra == \"dev\"", - "freezegun; extra == \"dev\"", - "mdx_truly_sane_lists; extra == \"dev\"", - "mike; extra == \"dev\"", - "mkdocs-awesome-pages-plugin; extra == \"dev\"", - "mkdocs-gen-files; extra == \"dev\"", - "mkdocs-literate-nav; extra == \"dev\"", - "mkdocs-material>=8.5; extra == \"dev\"", - "mkdocs; extra == \"dev\"", - "mkdocstrings[python]; extra == \"dev\"", - "msgspec; implementation_name != \"pypy\" and extra == \"dev\"", - "mypy; extra == \"dev\"", - "orjson; implementation_name != \"pypy\" and extra == \"dev\"", - "pylint; extra == \"dev\"", - "pytest; extra == \"dev\"", - "tzdata; extra == \"dev\"", - "validate-pyproject[all]; extra == \"dev\"" - ], + "requires_dists": [], "requires_python": ">=3.10", - "version": "4.1.0" + "version": "4.2.0" }, { "artifacts": [ @@ -4717,19 +4744,19 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", - "url": "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl" + "hash": "dd95840dd199baea12d9cc096a1d452caa6596a1c1e4b5f3dbd1541855d5e815", + "url": "https://files.pythonhosted.org/packages/0f/7b/39c34ca613b0b198cb866466651b26b045e2009864c5183c979a3b83f383/pytz-2026.3.post1-py2.py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", - "url": "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz" + "hash": "2211d3fcf9a797d3405cac96ac7f61d80e6a644f72a3309607282fe8a2010c5d", + "url": "https://files.pythonhosted.org/packages/fb/48/fb042503b6ca6cd271261dc559fd6432f7d8c713153e9ec5c591af4dfc1c/pytz-2026.3.post1.tar.gz" } ], "project_name": "pytz", "requires_dists": [], "requires_python": null, - "version": "2026.2" + "version": "2026.3.post1" }, { "artifacts": [ @@ -4893,13 +4920,13 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "47daa35a058c23468d6437f17a8c76882cb316b838ef763036af99b96cedd743", - "url": "https://files.pythonhosted.org/packages/fd/0a/c2345ebf1ebe70840ce3f6c6ee612f8fa749cfbd1b03069c53bf0c62aaad/redis-8.0.1-py3-none-any.whl" + "hash": "a4fe1aac3d3b3cc791d4b3d5931c5a956045dc951ee74d1c913ee3ac4d2ee9fb", + "url": "https://files.pythonhosted.org/packages/66/9d/c5731f6e3608663d4d3656fd8d3aecee8b509c3082818f5a13eae925baea/redis-8.1.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "afc5a7a2f5a084f5b1880dec548dd45be17db7e43c82a30d84f952aefb05cfb0", - "url": "https://files.pythonhosted.org/packages/cc/c3/928b290c2c0ca99ab96eea5b4ff8f30be8112b075301a7d3ba214a3c8c12/redis-8.0.1.tar.gz" + "hash": "6e1a19beef9225c83efd689c7e6b7da2d5215b1f42cd13b7fc3714d0a09c7b25", + "url": "https://files.pythonhosted.org/packages/a8/99/604f0b666d4c616d891cf77ebb9db6bb21601344c051aebf1b72b9ff915f/redis-8.1.0.tar.gz" } ], "project_name": "redis", @@ -4917,7 +4944,7 @@ "xxhash~=3.6.0; extra == \"xxhash\"" ], "requires_python": ">=3.10", - "version": "8.0.1" + "version": "8.1.0" }, { "artifacts": [ @@ -5476,154 +5503,154 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "2ce92b3748f02423e26d2bfb636fb9d7a8f67c8f5854dcae69d350d123b2eee2", - "url": "https://files.pythonhosted.org/packages/ce/6a/8b74c52ffd33dbbde00fe7251fee6a0acdc8cea33f7a43805aed258fb79b/simplejson-4.1.1-py3-none-any.whl" + "hash": "93f95f6769f37f4c3763666950b23bf9b9230b60bf1d718845e92db2ebcbf32d", + "url": "https://files.pythonhosted.org/packages/95/e2/7f47df8fbd3d9c35902b76c242e21759a27d6e8c030c901c4f0ad93dd10b/simplejson-4.1.2-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "2785ff8edc0e28bf773a32543a6bbed46351453c997b3f6709c744e3c2f7eabb", - "url": "https://files.pythonhosted.org/packages/05/88/bd8aad36b451ffb0e0a3f721d695a88befa6d1ac7d1e02ae788ca7ff4029/simplejson-4.1.1-cp312-cp312-musllinux_1_2_x86_64.whl" + "hash": "81c1746fcf232faec2b5b7160f8e249bb5826234286f861b7aa8e80faa0d98c4", + "url": "https://files.pythonhosted.org/packages/0b/e7/43a5331fa3918b239621a8258993e3ad5217861b345c6464ecf0a84076be/simplejson-4.1.2-cp310-cp310-musllinux_1_2_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "45ec18e337fec538b7e902d489505c450b2454653d1290f3f50385e6fd8aa607", - "url": "https://files.pythonhosted.org/packages/0c/b6/156a8de1e1b47694f0e7de6675866936608d45dc68388fd017d36f8693be/simplejson-4.1.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + "hash": "8842cae188daf4f7ab93cca85156bf57a1f5fe24379b9e20040da7d3a829ba64", + "url": "https://files.pythonhosted.org/packages/0f/e9/4d6181137f23088baa8abdc2a6e01a4eb872865532265cc4fae23f7dc193/simplejson-4.1.2-cp311-cp311-macosx_10_9_universal2.whl" }, { "algorithm": "sha256", - "hash": "c08eb9f7a90f77ae470e19a07472e9a79ebc0d1c2315d86a72767665bd5ba79f", - "url": "https://files.pythonhosted.org/packages/0e/2a/54837395a3487c725669428d513293612a48d82b95a0642c936932e5d898/simplejson-4.1.1.tar.gz" + "hash": "438aa043f693af285aaeea2ce72fa4a45b96d1532a1431d937577083fe13254d", + "url": "https://files.pythonhosted.org/packages/1b/47/53185d6bfa624dec5a9d1b10d4a9ca46b29124bc5695a1f33715a66606a1/simplejson-4.1.2-cp312-cp312-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c7494c75b95171194f965ea609e97081837a26494d91dcc046ad27dd9c3503e2", - "url": "https://files.pythonhosted.org/packages/1d/d6/a2a7a482fa43aaeaefc001491d381960f5e685ee4645343e0e037cebb57c/simplejson-4.1.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + "hash": "6f155b6b58725890f9a9d915b9e7108eaf7080542ab5fd8e31e9a5908ecf2c7d", + "url": "https://files.pythonhosted.org/packages/1e/5b/fa612786782b09373339ed89f1e80b0d667fdbb372534f0250bbceae1db8/simplejson-4.1.2-cp310-cp310-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "2867c64d92abd1992c15666fae198203093f593e43d6b81adf176bae530d493a", - "url": "https://files.pythonhosted.org/packages/1e/25/39013ffe279d90093ec1c848565b3683c586906c10fa55d9000ec29d046b/simplejson-4.1.1-cp311-cp311-macosx_10_9_universal2.whl" + "hash": "d63bc3adb552480c890b9ff8fc119e8997b414a8cb68b888761f5dfefafd2bf8", + "url": "https://files.pythonhosted.org/packages/20/dc/ad9aa843de7f9cb74a924f79a6d53986b3351206b792ccf27d6a4a92b745/simplejson-4.1.2-cp311-cp311-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "4c44ef6b02a4eb67ed17a72342341792149b3ff46f15426c26e970e49addf327", - "url": "https://files.pythonhosted.org/packages/34/09/69e331e3994b1ed9be6ce9ace4ade704e7ed503edf869929ca7bb404eda8/simplejson-4.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "a83dd08a7cbe770e9d14be2dbedde77890477cc44e804582ef69cbd8a1c834b2", + "url": "https://files.pythonhosted.org/packages/2c/67/dff3c64269e352dbec781f1920c4422816a675d1953b62d18c100ddf4931/simplejson-4.1.2-cp311-cp311-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "0e4b23f71dd781f8830f1663dc01a4944d3dbf87a1f93d78fba1cf64722d0ccf", - "url": "https://files.pythonhosted.org/packages/38/94/8d6f515b827b0f7881a49c8c1ac6920b7ae9428939ef04238c973278b42a/simplejson-4.1.1-cp311-cp311-musllinux_1_2_aarch64.whl" + "hash": "3b8d4ff4cd6e8cdab666431169378820cb4570fa06cb74d3f0cc165aa5434b62", + "url": "https://files.pythonhosted.org/packages/30/00/7876a15fbecac1d4653ce2af0b446fa1308af8d6f3f802e2e7019698b46d/simplejson-4.1.2-cp312-cp312-musllinux_1_2_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "7f61eefab86235c800e7f4e37d977080ec424bb2bf0b74e95a2d17ecb48eac0a", - "url": "https://files.pythonhosted.org/packages/47/da/3ba5e87e917094961e7b51b541c88f735f1ca37d580ac78a9302b468f64e/simplejson-4.1.1-cp310-cp310-macosx_10_9_universal2.whl" + "hash": "5589cd313b201cb915d796b6f9ad372b2285672a6bc9e9833bf23011297b38f6", + "url": "https://files.pythonhosted.org/packages/35/0c/eced473e9bdd47c741dcdfc1e309d243be970a9bf432d3f2e274f66f111d/simplejson-4.1.2-cp311-cp311-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "a9ab55d2459f6d0fdf9984a7a0fb0280dae12979f4fcc3171f5096a4fcf5fafe", - "url": "https://files.pythonhosted.org/packages/4a/3e/82c8997c4ef2ef6c832fbfc3bb2ed14a212616a284100af03b552ea7e072/simplejson-4.1.1-cp310-cp310-musllinux_1_2_x86_64.whl" + "hash": "ddbdfe504c8ab09443856544fb744dc881701b0289da68e3e6904ec1745ff4a6", + "url": "https://files.pythonhosted.org/packages/3d/57/8473951699049947b25c64e02f5897e37a4598a269a217d25e44bc35c571/simplejson-4.1.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" }, { "algorithm": "sha256", - "hash": "e294e33dbf316a9bbdd4030d46503c9b0f19470ae7ad6af5bae6c426bc2e869f", - "url": "https://files.pythonhosted.org/packages/4e/a2/6eebfb99dedc139f549200f61ade6d1890ac5707c5d427bdfa6fe39c9313/simplejson-4.1.1-cp311-cp311-macosx_11_0_arm64.whl" + "hash": "0247d71cd72dbd4f1b9dc3db9a1e333b74cbb35ee4f9287230de97de4cc03eb6", + "url": "https://files.pythonhosted.org/packages/49/77/31a91f9e237591802d72d9ff9922c4c88f94eef34ade81d6e36769d70ba6/simplejson-4.1.2-cp312-cp312-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "82bfca2b85a34178c25829c703f0a9e9f113a5af7539285bd3efb583a0bf1ba3", - "url": "https://files.pythonhosted.org/packages/5d/40/ed806f24afef295c1032448f5ff6f6f2979392d5645ddb9f4fed7f38194d/simplejson-4.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + "hash": "fd305a4699fc949ba6089ddfd17f582ce7c4a5d85e5601c44b42e35ebf521174", + "url": "https://files.pythonhosted.org/packages/4e/c4/75efe79679235b74ed3ff9ef91421bf4d57ca9e7ffa84543975f3fd833af/simplejson-4.1.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "19040a17154dc03d289bab68d73ce0a6a0be01de30c584bbdd93490bead14b22", - "url": "https://files.pythonhosted.org/packages/60/25/e90998fe8e480eb43b966c09e835379887d427567ebd496563d3b1e16b19/simplejson-4.1.1-cp312-cp312-macosx_10_13_universal2.whl" + "hash": "8c9850876ecf19c044d1f048fcb176997c0eb47ec9a9ba85d32d995fcf797168", + "url": "https://files.pythonhosted.org/packages/52/76/b3fe036455beda5e0ed0dd84dba674deb2c7b60f977b711394e5913250d9/simplejson-4.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "2e708d373a10e4378ef2d59f8361850c7150fd907ed49efe49bc5492160476d1", - "url": "https://files.pythonhosted.org/packages/76/0e/7f5a59d29426b062d5928fb88b403c3f797129d53be7102f955dbe51aa44/simplejson-4.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + "hash": "99a76da628af68a90c906e5c0dc7da784eb1ceff7e631aa7c43745b3e6d426a7", + "url": "https://files.pythonhosted.org/packages/57/bf/bd3582de3eeadd0f7d2864f37a91389f34e19d66391e9ef94bcf028be844/simplejson-4.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "980fc33353f81fd12d8c49d44f8c2760d1dc8192285e627c5180d141035b228a", - "url": "https://files.pythonhosted.org/packages/78/18/9943db224dd4d5fa3c090c3e56a94c37b254338c83995ec5680285111c40/simplejson-4.1.1-cp312-cp312-musllinux_1_2_aarch64.whl" + "hash": "5d7426d083647d034debf499094637f63257b72b9899c81aa8196444ee69580c", + "url": "https://files.pythonhosted.org/packages/58/dd/3253f207f0d7ce5fe4b78d76c70e1e9433c4fc94f40044ac4105ed6c6301/simplejson-4.1.2-cp311-cp311-musllinux_1_2_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "7ce252b28fddbdd83db5bd7d93dad2a8a591d7ada098afec9c1b23d6b722a7a4", - "url": "https://files.pythonhosted.org/packages/80/7e/c9e6c0c4ad8415e64dad0c47f619b556b02680a41631b4dbc281d55dc54d/simplejson-4.1.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + "hash": "dbc4f989e44d6153b5097357d260336d8021c21b3ae9d8954c9daf55788bfc16", + "url": "https://files.pythonhosted.org/packages/64/b6/82c2322d80345113a16451ecd7f2f487c380de0dbcfdbdefee7b99095cbf/simplejson-4.1.2-cp311-cp311-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "820c69a4710400e9b248d5670647d60be58824369282d3925e516b3ff1a7cd82", - "url": "https://files.pythonhosted.org/packages/86/1c/e4d0eab695be3eb21d0f46bce820752031f03e7113f9c80a9b3c73ee7157/simplejson-4.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "53b56770b5893d91b24f6ba985eb5bade5908971f62cb7f8e1ed50a9314601b2", + "url": "https://files.pythonhosted.org/packages/89/85/268d7abe6feb708c6ceb6651d28e8120d7d9ffb409e7aeb1f3acbe514e10/simplejson-4.1.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "a94ebaecdbaa80d9551a3ec6bf0c9302fc8b53ab6c1b2bfd498a1df4cb28158d", - "url": "https://files.pythonhosted.org/packages/9c/a0/abd4785f36c3400f1fbb21f517be39295a750a714f04b7ee175adf6ef580/simplejson-4.1.1-cp312-cp312-macosx_10_13_x86_64.whl" + "hash": "a769bce267ef4091b7ef380bcad1c474f4e66bfcfecb571e996215dcdd24df70", + "url": "https://files.pythonhosted.org/packages/8d/f9/69d2be0cbcdc1bd6052998b7d22cb3e7c80c9d6a371eb345cf079401fe41/simplejson-4.1.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" }, { "algorithm": "sha256", - "hash": "0662cfe0482c9796bd097213b27f006815bfdc9b671264c3c0b7fc0e72b71d00", - "url": "https://files.pythonhosted.org/packages/9f/b9/f830b648ae04601e6813306535d8e0a4c178d6453cec539b85dafdac80ed/simplejson-4.1.1-cp310-cp310-musllinux_1_2_ppc64le.whl" + "hash": "eaf2652770fc3415498cb7446bdb399624cc685df868d58e09d6722b63e937f3", + "url": "https://files.pythonhosted.org/packages/8f/3d/aa0c2337b17aeee27c24dc512f88db6093ffbe224d7107d6ef0d1a3f35da/simplejson-4.1.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" }, { "algorithm": "sha256", - "hash": "1778e09a6e4bb4ef304627915dc4a838569d9e6b737c787925b4e98244bbbc16", - "url": "https://files.pythonhosted.org/packages/aa/06/7a6482f336338dbdb6ca6d3099b2fdc1c74c47eea3c6511975751e9198df/simplejson-4.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "0be6cf935487b8d0bc97c528c89d0432f22e33ca1e1d4fea666d50d36b5afe4f", + "url": "https://files.pythonhosted.org/packages/9f/de/dc931f8621a3a6f58f3582e345f1a9e620f3d622f110be2a39e3343a488f/simplejson-4.1.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "67341c95c0a168ab4a6d1e807e50463f1c8da932c3286d81e201266c427061fa", - "url": "https://files.pythonhosted.org/packages/b8/78/fc060d2e3b13c6ec59288574b8efac64075e316b2afba4396a56b2422f78/simplejson-4.1.1-cp312-cp312-macosx_11_0_arm64.whl" + "hash": "b2162d7f5e2e9dc48c08af14d91481b9ffbb8d8420d7d1539209fa7f0ba8823f", + "url": "https://files.pythonhosted.org/packages/ad/9d/2b91d17c91638987dfe54bf3d2257370b235b66c1fe2fdb84e10767ac159/simplejson-4.1.2-cp310-cp310-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "de2ed102fff88dacf543699f53ee3a533cc11539a39baa176b7e09dd783069d6", - "url": "https://files.pythonhosted.org/packages/c2/08/9a690da9a766161c06c627d805362cf159f1abe480969372b2897649b955/simplejson-4.1.1-cp312-cp312-musllinux_1_2_ppc64le.whl" + "hash": "3733bf9afd79291eb0744b5b01041356df1b1bc25175a72519eb548751b61aeb", + "url": "https://files.pythonhosted.org/packages/b7/46/cd6f49506d633f5207c868c9373db594d4636b0fd69a2ce3dea53a1a05ff/simplejson-4.1.2-cp312-cp312-macosx_10_13_universal2.whl" }, { "algorithm": "sha256", - "hash": "b75c7ef874dbb350f41827cdf3cee23f5257bdcb0df46d4c01b34badb62dcfe8", - "url": "https://files.pythonhosted.org/packages/c2/2d/7832ed91cf4900f86c783d589bfac53358abfccb278f1c8b55eec167b395/simplejson-4.1.1-cp310-cp310-macosx_11_0_arm64.whl" + "hash": "d0b62163144dbc0dfc78df632a64ada3ce2e0760b65ead0f42e7d9c69be6f16f", + "url": "https://files.pythonhosted.org/packages/bf/50/5a6059682c6980d5f2b039aaa643d4d9e7272fc18eacfc7be30d922d20ba/simplejson-4.1.2-cp310-cp310-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "67e43e7c0555e10de6d83e1408035652fad28c983516e38c4e3a9a748c9af129", - "url": "https://files.pythonhosted.org/packages/c9/43/039982e956b06c6b019d48bdf9d4ec06f298adf6136552ad1979b94be0fd/simplejson-4.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + "hash": "78a0ff56305feec5217639247dba9174d20d4fa6538885a29b9910aad44fd92d", + "url": "https://files.pythonhosted.org/packages/d5/70/ff9cce6cdd7b2271ccf25e700a38986fa8ccf601fba405db28150414a7fb/simplejson-4.1.2-cp312-cp312-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "82fee635d7b73ad801030b05a75fbd34a098da0c2ecf600667a03636d09e1e42", - "url": "https://files.pythonhosted.org/packages/c9/fd/6dffb4956563d48bbe46b91ff341adae34920e94008fd6b8d728072abfc7/simplejson-4.1.1-cp311-cp311-musllinux_1_2_ppc64le.whl" + "hash": "c41397ba7c5d0a4ce2f058e8ebb0bf80703b0ba1cd1df9616399c35dd1a7df37", + "url": "https://files.pythonhosted.org/packages/d7/24/a0d405be16e7cbac5d1dab9b7f40a5c8dd681b546811047a8f102e53e125/simplejson-4.1.2-cp310-cp310-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "68e62eda21192c5ea9bb92d571ca46a4477fef48762f50d433de2b4253051551", - "url": "https://files.pythonhosted.org/packages/de/d2/a509ee37763e79aec75d68f8521db1440306edeba3b8b4064ab4ee8bf1d9/simplejson-4.1.1-cp311-cp311-musllinux_1_2_x86_64.whl" + "hash": "95569ac177885494967c1445a0fcc220eaa1ae53b71e852eb0d8689b683ca6d0", + "url": "https://files.pythonhosted.org/packages/d9/3a/bf9ab4db6f3c5e689acf5e3665fd1100413aa85604fd88bef879b2869b6e/simplejson-4.1.2-cp310-cp310-macosx_10_9_universal2.whl" }, { "algorithm": "sha256", - "hash": "93bf6653420258372444de90194dab8de8ff13d74b5d4263a5fefbbe8b8d2060", - "url": "https://files.pythonhosted.org/packages/f1/f5/e3ad592d089922abce2c2ea377548953ac55ffcbe061d600f01b9db2e6b6/simplejson-4.1.1-cp310-cp310-musllinux_1_2_aarch64.whl" + "hash": "f2156497f031d5c0051a335216f7ff5441746a93fbab5932e47105da7ef24ab9", + "url": "https://files.pythonhosted.org/packages/e7/3d/39e7253427dd4c2ec38596b944e3d3a53926cab7b3c2d28c43ef1a34d6d3/simplejson-4.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "4c47c46e16c8ea9e4850061e6ed5aa2b9cd2074cb2274bfd9c138cba15ce7453", - "url": "https://files.pythonhosted.org/packages/f2/ae/2c272971c8a87e2539c54a98eb6ff037bee1e2e93943c3986cf7500a4f3a/simplejson-4.1.1-cp311-cp311-macosx_10_9_x86_64.whl" + "hash": "630160f22edb2b13e05e0fe13dd80c2a064374f212696d3ee472f9ee13ebf4ac", + "url": "https://files.pythonhosted.org/packages/ec/dd/1ca3b57840863954e1d402287f8481c88f4e84bdc1cd27f6a0f3a7097b69/simplejson-4.1.2-cp312-cp312-macosx_10_13_x86_64.whl" }, { "algorithm": "sha256", - "hash": "4484960512db9c8124bfa91e0d8a9f9c302338f1c5454e74c21d7d022df10f46", - "url": "https://files.pythonhosted.org/packages/fe/8a/d0c08f4b8934b64469a63d461a68a01d5cc32faf313400dda2bdc1075a29/simplejson-4.1.1-cp310-cp310-macosx_10_9_x86_64.whl" + "hash": "6ae4186f90362e9c03c80a1cd5062a20f3a11ac9d391f7ee0ef0701a0e2b7394", + "url": "https://files.pythonhosted.org/packages/f1/e3/1cc7dbf4deebc16e9dc42db37f473b5b612d021eb10e69974be308425171/simplejson-4.1.2.tar.gz" } ], "project_name": "simplejson", "requires_dists": [], "requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=2.7", - "version": "4.1.1" + "version": "4.1.2" }, { "artifacts": [ @@ -5665,19 +5692,19 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", - "url": "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl" + "hash": "8089a26fd974ca7a1f30276d3d8492ab266ab15af581642dfe8aa162e0c1c823", + "url": "https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", - "url": "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz" + "hash": "4a55d8cf158a9c2e587fa4922f1bbb91d68ac829e2d6f25403a85747c71daf74", + "url": "https://files.pythonhosted.org/packages/69/99/a6ca3beb3ccacb41fb3321d8a60e5566f9e6467601ef8eba6a17e1b89778/soupsieve-2.9.2.tar.gz" } ], "project_name": "soupsieve", "requires_dists": [], - "requires_python": ">=3.9", - "version": "2.8.4" + "requires_python": ">=3.10", + "version": "2.9.2" }, { "artifacts": [ @@ -5955,21 +5982,21 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", - "url": "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl" + "hash": "65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", + "url": "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", - "url": "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz" + "hash": "547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", + "url": "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz" } ], "project_name": "typing-inspection", "requires_dists": [ - "typing-extensions>=4.12.0" + "typing-extensions>=4.15.0" ], - "requires_python": ">=3.9", - "version": "0.4.2" + "requires_python": ">=3.10", + "version": "0.4.4" }, { "artifacts": [ @@ -6341,31 +6368,31 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85", - "url": "https://files.pythonhosted.org/packages/96/42/3e5985a0a7e57de470b320c6d6a1a67c844f6737a587f3d44dd13d1819e7/wcwidth-0.8.2-py3-none-any.whl" + "hash": "d5b73dba6158a595ec9370350e7f2637bcac8d6c5e4fde34f30fcffb6103a5e4", + "url": "https://files.pythonhosted.org/packages/c4/0e/57f6bb3024a597b2e8ec4aee710ffe62ddc95af2e2bb1ee7a7abdc22c68c/wcwidth-0.8.3-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "91fbef97204b96a3d4d421609b80340b760cf33e26da123ff243d76b1fda8dda", - "url": "https://files.pythonhosted.org/packages/34/74/c6428f875774288bec1396f5bfcbc2d925700a4dad61727fd5f2b12f249d/wcwidth-0.8.2.tar.gz" + "hash": "d128512515fbf4612e0ff21fd6380399210318b7b54a9af59dff8454cf9730eb", + "url": "https://files.pythonhosted.org/packages/36/57/ed58088fafdf4c55a0ad6bde846502567645424d7ebf325230b9237f4085/wcwidth-0.8.3.tar.gz" } ], "project_name": "wcwidth", "requires_dists": [], "requires_python": ">=3.8", - "version": "0.8.2" + "version": "0.8.3" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "e68ad87fda378191081965ab02a185391c26e4e926adec855c3b0286a8369d49", - "url": "https://files.pythonhosted.org/packages/83/21/fce134877fb6fc6ad3c464e4a07ede0ee9219f705d26a981ae58ea36ca13/webob-1.8.10-py2.py3-none-any.whl" + "hash": "4addd1d38d6a7fbe0eda22d45f25a40d74c8b290f3a99c0ac3d4023cf21f2da2", + "url": "https://files.pythonhosted.org/packages/c1/71/370a65bc7726a1cbf478445c898c7affe907aa42e2e9e5bb38319db74115/webob-1.8.11-py2.py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "1c963a11f307bc3f624fbab9dde737701eae255f32981b7a5486a88db1767c2b", - "url": "https://files.pythonhosted.org/packages/58/f9/974eafebfd0bd442b8848899fe7d30675c93f750c313e1a6fe61acbde1e3/webob-1.8.10.tar.gz" + "hash": "aa8c27231070b135c025e567a9cd7eda03f4df71352ffaac740cb6a75f0f81a5", + "url": "https://files.pythonhosted.org/packages/e6/92/9329872e36be90e25ba616ac6e9ee8c1be4326a1ea3fdffcdabd0eb48efb/webob-1.8.11.tar.gz" } ], "project_name": "webob", @@ -6380,7 +6407,7 @@ "setuptools; extra == \"docs\"" ], "requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7", - "version": "1.8.10" + "version": "1.8.11" }, { "artifacts": [ @@ -6438,13 +6465,13 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "212281cab4dff978f6cedd499cd893e1f620791ca6ff7107cf270781e587eced", - "url": "https://files.pythonhosted.org/packages/87/1b/9e33c09813d65e248f7f773119148a612516a4bea93e9c6f545f78455b7c/wheel-0.47.0-py3-none-any.whl" + "hash": "3217dcc807155e45db462d7ef2431f5ddda0d7273b700d05a67b271ceb1287ab", + "url": "https://files.pythonhosted.org/packages/2e/29/69cfbb602cd91690c55d38ba9fe53e6a7e76a6fa647bf38f19c138d25449/wheel-0.48.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "cc72bd1009ba0cf63922e28f94d9d83b920aa2bb28f798a31d0691b02fa3c9b3", - "url": "https://files.pythonhosted.org/packages/39/62/75f18a0f03b4219c456652c7780e4d749b929eb605c098ce3a5b6b6bc081/wheel-0.47.0.tar.gz" + "hash": "94800765601e9171bf5d58d066e640662842bcedcbab982b2c90787a2c987322", + "url": "https://files.pythonhosted.org/packages/d0/20/50ed6bdf27dec98b568a8ae25dc599f35baa3d9709f9e83fd1edb56b9a90/wheel-0.48.0.tar.gz" } ], "project_name": "wheel", @@ -6452,139 +6479,139 @@ "packaging>=24.0" ], "requires_python": ">=3.9", - "version": "0.47.0" + "version": "0.48.0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "5bad217350f19ce99ca5b5e71d406765ea86fe541628426772b657375ee1c048", - "url": "https://files.pythonhosted.org/packages/6e/d2/6317eb6d4554855bbf12d61857774af34747bf88a42c19bf306de67e2fa3/wrapt-2.2.2-py3-none-any.whl" + "hash": "d8c7ed08477429752b8c44991f40ad7838b18332a160698740a6bfbc10d998a2", + "url": "https://files.pythonhosted.org/packages/00/39/3daf9f47be208606586de4568ba6713db53ebc8fd7a575aea1fe57983b69/wrapt-2.3.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "4bccea5cdecffa9dd70e343741f0e41e0a16619313d04b72f78bb525162ebcd0", - "url": "https://files.pythonhosted.org/packages/01/df/2def7e99d1fe87eea413f95f671924cdddcb08823b1ffd212748dfa6d062/wrapt-2.2.2-cp311-cp311-musllinux_1_2_aarch64.whl" + "hash": "5ab559e1b2551d23d54db2a0001c6d73bad022a254639561c5f6c382a9d6c2fe", + "url": "https://files.pythonhosted.org/packages/00/b8/9182e4c618a847be0baccb68e4602b070d0fa22c782cf058f4bc66b32709/wrapt-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "e5a5290e4bf2f332fc29ce72ffb9a2fff678aaac047e2e9f5f7165cd7792e099", - "url": "https://files.pythonhosted.org/packages/02/49/bc0086292d239575b4c08f4cf8a4079fa58abbad58ec23abf84833a283ed/wrapt-2.2.2-cp311-cp311-musllinux_1_2_x86_64.whl" + "hash": "3d1c2c1b808600d2ea808e6360910a60ed5f409a4011655e10f9164ba0a414a6", + "url": "https://files.pythonhosted.org/packages/0e/60/9bda34c3d7d182aa703fe35339ae0ed4c4dad5e5c587f93890143e1f87fb/wrapt-2.3.0-cp311-cp311-musllinux_1_2_riscv64.whl" }, { "algorithm": "sha256", - "hash": "6d1a6050405bf334be33bf66296f113563622972a34900ae6fa60fd283a1a900", - "url": "https://files.pythonhosted.org/packages/0b/43/894f132d857ed5a9904d937baf368badcbe5ea9e436e2f1930fe21c9f1f0/wrapt-2.2.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "e5301c35cf75655eb33498f2bd6ae8703ca19940e3167dc9cdf740c712a39c60", + "url": "https://files.pythonhosted.org/packages/12/1e/8eded8615d39e3ce81f626937a3a87b280a2a86239a2bf14a4b4bb345034/wrapt-2.3.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "656593bb3f5529f03d27af4136c4d7b11990e470bcbc6fefa5ef218695bece55", - "url": "https://files.pythonhosted.org/packages/23/1b/545eee1c18f3af4cf140bb5822b6ef81ebe569df0a63ac109973103a30a5/wrapt-2.2.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + "hash": "e2e692bc0d63f881cf7006730a56bd4e0c2fab5dc318466942805d692b166276", + "url": "https://files.pythonhosted.org/packages/1f/d6/c66b4ba4eda49257c84d5c2df26118280f09ca7905aee20d0064db778d13/wrapt-2.3.0-cp310-cp310-musllinux_1_2_riscv64.whl" }, { "algorithm": "sha256", - "hash": "f1a2ff355ece6a111ca7a20dc86df6659c9205d3fcee674ca34f2a2854fd4e73", - "url": "https://files.pythonhosted.org/packages/27/15/0c2d55168707465abfc41f33c0b23d792a5fa9b65c26983606940900a120/wrapt-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl" + "hash": "816877aa749253149f9ecfd2635d4d948ecfa338e1a0311d187b1acb1bb8a3eb", + "url": "https://files.pythonhosted.org/packages/23/41/c35940ea1c423f129ebe4361db853bc80d4def6326242e1206fa15bf94f4/wrapt-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "10adb01371408c6de504a6658b9886480f1a4919a83752748a387a504a21df79", - "url": "https://files.pythonhosted.org/packages/29/de/3c833e03725b477e9ea34028224dd21a48781830101e4e036f77e8b6b102/wrapt-2.2.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + "hash": "5d221a6e6ddd302b8397433184e96b59f259f50024b854db1c411a881586b6b8", + "url": "https://files.pythonhosted.org/packages/28/7f/cfd9bc4b1f5e424eeea83d0493e43f3b1b02707ce8e50c47945873982bd5/wrapt-2.3.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" }, { "algorithm": "sha256", - "hash": "8417fd3c674d3c8023d080292d29301531a12daf8bd938dd419710dd2f464f2b", - "url": "https://files.pythonhosted.org/packages/2a/85/180b40628b23772692a0c76e8030114e1c0ae068470ed531919f0a5f2a4a/wrapt-2.2.2-cp312-cp312-macosx_10_13_x86_64.whl" + "hash": "681a2d0eefd721998f90642762b8e75c2159ec531b20ad5e437245ea7b06a107", + "url": "https://files.pythonhosted.org/packages/2b/b0/c1f5a970721f06b85c0cd5142e0ff8fe067708abd779b0c4f4be7d61d09f/wrapt-2.3.0.tar.gz" }, { "algorithm": "sha256", - "hash": "3442eee2a5798f9b451f1b2cd7518ce8b7e28a2a364696c414460a0e295c012a", - "url": "https://files.pythonhosted.org/packages/33/be/27edce350b24e3054d9d047f65f16d4c4d4c1f3f31c4278a1f8a95c723c8/wrapt-2.2.2-cp312-cp312-musllinux_1_2_aarch64.whl" + "hash": "418f54bb09d1762db02c7009b4051149893af3153a87f92d70356703c11eea02", + "url": "https://files.pythonhosted.org/packages/35/ea/a0af2d9da62897af2a055484920de05dade30d2ba2c0d65cbdea875d3d8b/wrapt-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "a2d78c363f97d8bd718ee40432c66395685e9e98528ccaa423c3355d1715a26d", - "url": "https://files.pythonhosted.org/packages/34/f3/de70937472dd3e8a4e6811192f9c6075efdffd4a2cd9b4596bf160f89668/wrapt-2.2.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + "hash": "0bb2797048db0956348cb3058c33bc4184614f13231389cfbccc16a5d32780a7", + "url": "https://files.pythonhosted.org/packages/40/31/5822ce37ca8820c2ed35a498c67c8b37960b9cee2ba437fd32849d0a234c/wrapt-2.3.0-cp310-cp310-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "dfb00cb7bb22099e2f64b7340fb96113639aa7260c0972af3797ace2297b936c", - "url": "https://files.pythonhosted.org/packages/44/a7/6f42a3d03e44dc612a5dcff324e7366075a7857f0be2d49a8cb8a68279b8/wrapt-2.2.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "d0077f3d65541925fa83002f967b22ad6550d24813ac64cb905f717194128d9c", + "url": "https://files.pythonhosted.org/packages/49/2c/bc508fee75eb2919ed69769800b09968e4aab16897f909a23f39c81e323f/wrapt-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "518b0c5e323511ec56a38894802ddd5e1222626484e68efe63f201854ad788e5", - "url": "https://files.pythonhosted.org/packages/45/04/aa5309beed5344b00220ae6b3b24055852192656194c27947bee1736306a/wrapt-2.2.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + "hash": "9790ea25190a4e0fe4cdf4eeb868e9d75f8a024a70a5b6bf9c348a3a2b72e731", + "url": "https://files.pythonhosted.org/packages/4d/e5/04f34d38e66d857dfc2fc4088d60e70c0e422467822defa49b2b4a26e17b/wrapt-2.3.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "2e096c9d39a59b35b63c9aacfbbbec2088ff51ff1fc31051acc60a07f42f273a", - "url": "https://files.pythonhosted.org/packages/5f/b3/7e6e9fcf4fe7e1b69a49fe6cc5a44e8224bab6283c5233c97e132f14908e/wrapt-2.2.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + "hash": "0a45ffae742ce91a16e11cb6c7cd71e7f9994f3cbd283b962ab093f5c6dcf525", + "url": "https://files.pythonhosted.org/packages/5b/4a/d17a0fad1bf1c5f2c887ff71fef75654141b0880bff71d157d955b5bec3a/wrapt-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl" }, { "algorithm": "sha256", - "hash": "55b9a899e6fff5444f229d30aa6e9ac92d2216d9d60f33c771b5d76a760d5f8e", - "url": "https://files.pythonhosted.org/packages/7d/b5/5c0b093eb48f8a062ef6267d3cb36e9bb1b88440181f6545a383c60efdf8/wrapt-2.2.2-cp311-cp311-macosx_11_0_arm64.whl" + "hash": "fc82c2ccc8e234c844f5303d9f2984b346dcdd53e94823ce8420d2c75b4b9023", + "url": "https://files.pythonhosted.org/packages/6c/21/1f780bba935dcf697c0c59de9be3a559bbb8e31a53ca3f25422023738432/wrapt-2.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "4402f57c5f0d0579599858ffbdd9bf4e3f0972f51096f2bd6cc7dab6b76ee49e", - "url": "https://files.pythonhosted.org/packages/84/9b/12e23264d8f4735e8483262f95c5a6b03c3665fd2a84bdf99a45b6a2f4ec/wrapt-2.2.2-cp310-cp310-musllinux_1_2_aarch64.whl" + "hash": "69e477046f2237ef0bc6547544ee73008dc764ca26eff44f09e976d221b34d5d", + "url": "https://files.pythonhosted.org/packages/6e/55/51b92daaf6defb57f4dc56bdcce985400f75c6984a03ca5e78ccac717028/wrapt-2.3.0-cp312-cp312-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "8374eb6b1a58809211e84ff835a182bb17ab2807a5bfef23204c8cff38178a00", - "url": "https://files.pythonhosted.org/packages/94/dc/66c61aca927230c9cf97a3cb005c803971a1076ff9f7d61085d035c20085/wrapt-2.2.2-cp310-cp310-macosx_11_0_arm64.whl" + "hash": "fc648a335d7e01adb3640b25f02fd0ea05886cf04d0af7f4ee902bc7b5e466e8", + "url": "https://files.pythonhosted.org/packages/71/71/4cd2151a236f44a6e2dd4ed8011838d7ba0be3d656c8bafdfc65a2ed1917/wrapt-2.3.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" }, { "algorithm": "sha256", - "hash": "0e7070c7472582e31af3dfc2622b2381a0df7435110a9388ed8db5ffbce67efb", - "url": "https://files.pythonhosted.org/packages/94/f2/21c90f2a16689702e2aaff45795b11018dff2c9b1242bac10d225483f676/wrapt-2.2.2-cp312-cp312-macosx_11_0_arm64.whl" + "hash": "a6e19531ae33c508cea7d84a7edfda01fa86e51b8d1a93a77712c55e6e469152", + "url": "https://files.pythonhosted.org/packages/73/31/6c7799d7b6431fcd7e1b83245fb45258a2d2c3a2187fbaecb83572a72d7a/wrapt-2.3.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" }, { "algorithm": "sha256", - "hash": "d619e1eed9bd4f6ed9f24cd61971aa086fa86505289628d464bcf8a2c2e3f328", - "url": "https://files.pythonhosted.org/packages/a5/ec/40aed2330e7f02ecf74386ffcfef9ccb7108c6a430f15b6a252b663b1bed/wrapt-2.2.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + "hash": "ce9f398f868d2b3b27aa2ea4de79645ef9077aeeac8dfc2814b0d542c6a2b87f", + "url": "https://files.pythonhosted.org/packages/7a/5a/3c6117938be98754578ab83f5a40d7d0ea2cd2c487dc5cd6027ee7228229/wrapt-2.3.0-cp310-cp310-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "3b686cfc008776a3952d6213cb296ed7f45d782a8453936406faa89eac0835ab", - "url": "https://files.pythonhosted.org/packages/bc/c2/aa6c0c2206803068c6859dabe01f8c84c43744da93d4c67b8946d21655ee/wrapt-2.2.2-cp312-cp312-musllinux_1_2_x86_64.whl" + "hash": "1598becd30f8f2777d18564064eb4f4dbe1ab0e05a8f09786d0ef505ac782bf3", + "url": "https://files.pythonhosted.org/packages/7e/dd/63cd4c864c65ef4906df64bd2d378f4a62b54f28063f282dfb3bf93caead/wrapt-2.3.0-cp312-cp312-musllinux_1_2_riscv64.whl" }, { "algorithm": "sha256", - "hash": "e7f10ee0bd53673bfd52b67cbce83336fe6cad90d2377b03baf66491d2bbfb91", - "url": "https://files.pythonhosted.org/packages/bf/55/4d76175aaa97523c38f1d28f79d18ab41a1b116814158a818bc0eba00571/wrapt-2.2.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl" + "hash": "bff9a671bc00709cab5a7f745c592b5671873449db0ee2a569af994f16b29a4d", + "url": "https://files.pythonhosted.org/packages/84/ca/613cefd9c5977366b1587e61c0b428176d382e6d75b454084c5e58503042/wrapt-2.3.0-cp311-cp311-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "209112cafd963710a05d199aae431d79a28bc76eb8e6d1bbbb8ad24340722cae", - "url": "https://files.pythonhosted.org/packages/c7/f6/a906d01a2ce12157bad2404957b3e2140da354b8a70b2fa48bbf282871c0/wrapt-2.2.2-cp311-cp311-musllinux_1_2_riscv64.whl" + "hash": "ad71df7a04dd3497e9302e81f4a7c91bd401ea0e15a9df9029527900f94bee43", + "url": "https://files.pythonhosted.org/packages/a5/0f/94ae724c5087eb6054c0d63febd7094947dcf302fe058e2e0488102a872b/wrapt-2.3.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" }, { "algorithm": "sha256", - "hash": "3a4eb7964ff4643d333c84f880bcf554652b2a1050aebc54ae696327f61acfaf", - "url": "https://files.pythonhosted.org/packages/d6/a3/bcd5ec37289dcd85ecd4d15395a6a6063d60bc45ff94a9d77814e1e54d64/wrapt-2.2.2-cp310-cp310-musllinux_1_2_riscv64.whl" + "hash": "c8388ba7faf5dbf9ee106bb70d66f257629b1bd98091123e19e8a4553a319199", + "url": "https://files.pythonhosted.org/packages/c0/f2/1a3b949c0322fb27396eafd1044328c1cb0400e0b32105d75a3cd03096e7/wrapt-2.3.0-cp310-cp310-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "055e6fcfaa28e58c6a8c247d48b92be9d56f818b7068aa4f22b15b3343a09931", - "url": "https://files.pythonhosted.org/packages/e1/8b/59781d0fe7b0adfbea37f600857de4be68921e454aeecf1a11bda35cdccc/wrapt-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl" + "hash": "3da470536bf9645143323dd41b32db55c6f4304ad382094c1a1da8a92061e10d", + "url": "https://files.pythonhosted.org/packages/ca/ee/82f1fc9e431b5c2c5a6d201aa865dbeae3984c311c6d11a185f0c8367cf6/wrapt-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "6c99012a22f735a85eed7c4b86a3e99c30fdd57d9e115b2b45f796264b58d0bf", - "url": "https://files.pythonhosted.org/packages/e2/c4/9fd9679af8bf38e146652c7f47b6b352c3e5795b4ad1c0b7f94e15ac2aa7/wrapt-2.2.2-cp312-cp312-musllinux_1_2_riscv64.whl" + "hash": "392158c9a7f2ab1b8699418bfc0fe6f83548788c418b27d7bf2019ad3405cebb", + "url": "https://files.pythonhosted.org/packages/cb/89/ff7814f6eb6856b479946117d1138a2fbb46cdb6b1f379db359056c69743/wrapt-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "e542b7c5af91e2123a8aabf19894319d5ec4268d2a9ffd2f239386133fc47746", - "url": "https://files.pythonhosted.org/packages/f2/be/716d708f607fa70f8a6eb47dff8ee945d5278dfc89ffeeff33039d052e63/wrapt-2.2.2-cp310-cp310-musllinux_1_2_x86_64.whl" + "hash": "df4ce31150bcd5d9f36f816aac3010ab4f4bf8672ac1d3b0ac7d539ec61c7c02", + "url": "https://files.pythonhosted.org/packages/ce/17/42d670dbfafd49076c6eb2b7d67633d7e1c968e39bfb11a135acb6fac67b/wrapt-2.3.0-cp310-cp310-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "0788e321027c999bf221b667bd4a54aaefd1a36283749a860ac3eb77daed0302", - "url": "https://files.pythonhosted.org/packages/fe/a4/282c8e64300a59fc834518a54bf0afabb4ff9218b5fa76958b450459a844/wrapt-2.2.2.tar.gz" + "hash": "5ba1e5e08ddc46130e9682b2c249f2d1dd39bda9106ed4bd401b7519f18f41bd", + "url": "https://files.pythonhosted.org/packages/e8/ba/60bfd9b1a751f4fcb2d603668fc272d651ccdd339a56acf8c40ad21a0293/wrapt-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl" } ], "project_name": "wrapt", @@ -6593,7 +6620,7 @@ "setuptools; extra == \"dev\"" ], "requires_python": ">=3.9", - "version": "2.2.2" + "version": "2.3.0" }, { "artifacts": [ @@ -6738,83 +6765,83 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "f0b48ccadaa9839e09ff81e969703cecb3f402c813bfe8b958652e699bea69f5", - "url": "https://files.pythonhosted.org/packages/30/4c/8b56259558cace4414e753ca6740396a1f59d4a95ddb55b4658600408670/zope_interface-8.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" + "hash": "28b68c24131545c1d13fd2178bbd065e67f09db885d8426adf1fbdf2b6b66372", + "url": "https://files.pythonhosted.org/packages/05/89/7767a6f9b0bb41a4d3777e8f93bfeb1b9a23ea643f83ce96163d9d672c8b/zope_interface-8.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" }, { "algorithm": "sha256", - "hash": "bf917009a4a7457c7290225a019f4a0aa706d96accd2cfdba2418d3bc1fcde2f", - "url": "https://files.pythonhosted.org/packages/03/3e/1774b0ee46ccbb5498ee3c33ece40315b6ef58bc71957be94bd345340bc1/zope_interface-8.5-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl" + "hash": "6246f7a4b196bd054469f4fd4ffdac307974061f0d2b1ef4da87ddff13a7f885", + "url": "https://files.pythonhosted.org/packages/00/9c/53361a3cbf7ae2529d69a252547b129afa44f75d7019a76b15cecad44c91/zope_interface-8.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" }, { "algorithm": "sha256", - "hash": "147a9442dcc2b7339ecdb1be2b3cdb098e90462e39425054053ebfb50d99125a", - "url": "https://files.pythonhosted.org/packages/06/61/e6501d8ea7a2cac3217e03f404e1f98c1df7191d83cfe86b1895fbba5dac/zope_interface-8.5-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl" + "hash": "42fb95008784a3b50c4b79e4488845d1950c57eef17ebc9c53a680084fb93da2", + "url": "https://files.pythonhosted.org/packages/11/56/4cf64b40123af62ed9dad3d689df853d9ff8f431ae06408e087061a9b87a/zope_interface-8.6-cp310-cp310-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "7a3ba1c5877f0f3e3906b02ddf793abed2becc2948116414ce0e1dd820b68d6d", - "url": "https://files.pythonhosted.org/packages/08/dc/50550cfcbb2ea3cbca5f1d7ed05c8aa840f831a0f2d63aec0a953f7c590e/zope_interface-8.5.tar.gz" + "hash": "a38b221cc649a2daacaff9d629a2ba9c4a8967669d253f9a6a597f46d46732f0", + "url": "https://files.pythonhosted.org/packages/17/4f/41bde1796fa8cbb32f50facd261dd4124daa850c29666270e85e2bb8e91a/zope_interface-8.6-cp312-cp312-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "efe234a0fafb4b6b1602e9be9245b97c2bf06d67c07af5a4bc3c0438978b555c", - "url": "https://files.pythonhosted.org/packages/17/0f/8f1a29966bcf863e3a2121edcafb81c55715de7886bcc9544749cc79e7da/zope_interface-8.5-cp310-cp310-macosx_11_0_arm64.whl" + "hash": "826f99c38f4bfcf7165885a0c59f03c6c25e0df8cdb0544f882cda61616fe845", + "url": "https://files.pythonhosted.org/packages/21/00/27467685e40d5ee01f542c8b0b33682b07af363419f4c64cbb61b8bf48d5/zope_interface-8.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" }, { "algorithm": "sha256", - "hash": "03bbecc7982af713d7499d4084bc03916413d17ffd45f89009348cc0c1d9e376", - "url": "https://files.pythonhosted.org/packages/37/36/7599ecabcf80ce4fef2e1ef3c5ac0d4696b61f03f724cc44022f4d226af9/zope_interface-8.5-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl" + "hash": "b40ef9b4873afb5d0dec02b8d2dfde1cf18c72337b60c99cb735961e0bac05c0", + "url": "https://files.pythonhosted.org/packages/26/39/a8481b926e42c44a6fcc670904f8251469ec42edbff1ba066719ca1e7fb4/zope_interface-8.6.tar.gz" }, { "algorithm": "sha256", - "hash": "c1adc90d3576b3b4c4de4953e6002c37bef28b78d7fa54c1bbfd0c50f022fe7c", - "url": "https://files.pythonhosted.org/packages/4e/78/cbceec44f1b27208a76c1a688c131302685852406a23df5aab68324109cc/zope_interface-8.5-cp312-cp312-macosx_11_0_arm64.whl" + "hash": "6df4bd16923d247c34e12dc394dab20d99d96aa2e15a6b163c2dda1dd582fff6", + "url": "https://files.pythonhosted.org/packages/2e/02/c82afe287924ddcbc78fd2f4646f16e3ba9d03bf3f5e1ea4bedce711de3c/zope_interface-8.6-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl" }, { "algorithm": "sha256", - "hash": "a17e681224267880707c9ec9e730ad9a1ad2d65c371256843efba6cf48711b58", - "url": "https://files.pythonhosted.org/packages/91/15/bfa25ef480b02af6e9452c478483fec75e87c9e2b60c407fd0b1f6054b9c/zope_interface-8.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" + "hash": "a43e669d68fd8c10fe315812f7e1d262c6c00e9667f29f799a3771f9a3b5b41d", + "url": "https://files.pythonhosted.org/packages/41/7d/d3abda21695ee441f2278f226b4b22ecb604cf0d96efb3d39507415abdcb/zope_interface-8.6-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl" }, { "algorithm": "sha256", - "hash": "8e6ee90c2e6de7c37058d5fa41f123c8b13a312db8d1e0fb5840d7f4bcdff9c9", - "url": "https://files.pythonhosted.org/packages/97/cc/b84123a948f3162a34623e188922827cd845244fdd043ed20f8d02228caa/zope_interface-8.5-cp312-cp312-macosx_10_9_x86_64.whl" + "hash": "3e0383361da2793ea332e2d12b753a32ac57b3b89c8c3a9c6dd04374ae142c0f", + "url": "https://files.pythonhosted.org/packages/65/7a/80fa8eb011bf78b72438a0ea0f672efe97ac5c9e9ff07d3304143ef5d611/zope_interface-8.6-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl" }, { "algorithm": "sha256", - "hash": "dabeb6fe1228d411994f300811edc6866fff0cdcbc9cef98a78f05ea0da42e37", - "url": "https://files.pythonhosted.org/packages/9f/9f/37e564eaaf85e3abc1ada40a79fa43f2ab45bdb67431b0ec0fe29e4763e2/zope_interface-8.5-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl" + "hash": "919510e0d470c189cb84164b953f81e8a513aa2593fdc9e4982340838cd1099b", + "url": "https://files.pythonhosted.org/packages/85/55/3092a23c3bdbcc9402ad74e69dae3fa49cc9f12bceef35079c98449bc60e/zope_interface-8.6-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl" }, { "algorithm": "sha256", - "hash": "31cff25b2aaedb5267e6e77b1e9be6b0ec4f622032de8a069202b8ffacda7dc2", - "url": "https://files.pythonhosted.org/packages/b6/09/e533b2ffabaae4e5d5730d6768a591cf335defe8e37bec2ad905d09be656/zope_interface-8.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl" + "hash": "a2c5963a26e1fe47bdb3494ba2aa91904c7898873af400dc3bdcaa808a57783a", + "url": "https://files.pythonhosted.org/packages/90/b8/59d9a455a73cae715eacca70003d943e8a8413b196454bb49530c16769bd/zope_interface-8.6-cp310-cp310-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "c0c8aa2bf8f3911ef37b87deb1bbe225a310e6eb6522a16d77f5d8330c4f6fbe", - "url": "https://files.pythonhosted.org/packages/b6/43/9cd98bee951d23848de690ba2809f87e3b22c67c370987acc960da15ad37/zope_interface-8.5-cp310-cp310-macosx_10_9_x86_64.whl" + "hash": "780a66db884c0e2b0e6b34b4900f86916945a7c03d3be40ec845b051fcc052cd", + "url": "https://files.pythonhosted.org/packages/98/e1/b2d78ecb8aec59114111ed8c25894c0421afecc5e89b36fc356e2b07a607/zope_interface-8.6-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl" }, { "algorithm": "sha256", - "hash": "c28044972187245d7a309e4699319bfdbd2ffcbf7176d1d4ddf5adffb2dea80f", - "url": "https://files.pythonhosted.org/packages/bb/a7/6b6e0c31ac240cb9fc015ae9ed45ca54be886c18fcf7bfa2377a4d7a8785/zope_interface-8.5-cp311-cp311-macosx_11_0_arm64.whl" + "hash": "ebb513c9e47702525897148e38271f7b6bf12c61bd084cdddfd0e03b542f8100", + "url": "https://files.pythonhosted.org/packages/a2/9b/60a71a998fd819a7b9ed24c3544f862280f222828f421561e28885dcecc5/zope_interface-8.6-cp311-cp311-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "5e970dabea777a24b0b0bbf9dae3ab75ce8b2d8e948edf4875627034b21f3560", - "url": "https://files.pythonhosted.org/packages/c5/66/1036543d6a66bc04c19df3cf650f3ad938a002ab0a443c24e23e8de5e8b9/zope_interface-8.5-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl" + "hash": "192bb756a8f62395b4fe47cbb853c171f20389d5226fbfa97128bb2f76abad8d", + "url": "https://files.pythonhosted.org/packages/be/0a/33bcf5c825c749205c832e82d14224ff38011d20dd9dbf7a0ffe51a589ae/zope_interface-8.6-cp312-cp312-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "afc66ccaef2a3c0bef6ca02aad40d29a39276389dad16a8eac36f9f385e4d057", - "url": "https://files.pythonhosted.org/packages/ce/f1/83ad110fb847413affe71609bb50e59e1aa082e1236030122227c7c283d3/zope_interface-8.5-cp311-cp311-macosx_10_9_x86_64.whl" + "hash": "9217b1123f6aeec9ddf1789bffd83da3123546d551c164a99f862a5d1f5ac0f8", + "url": "https://files.pythonhosted.org/packages/dc/5a/126eeee4da016f5cca4db2297496069d5f1ba901fb53ebf104f9c087a113/zope_interface-8.6-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl" }, { "algorithm": "sha256", - "hash": "e6347b8d8d12c5eca6502450a92be30079b7acfade2c4f693efa0deb8871b06e", - "url": "https://files.pythonhosted.org/packages/e1/c3/005032195ff3b210c139b7c560ed5c534e844b0907d8e44d2b3d8919305e/zope_interface-8.5-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl" + "hash": "dd25d6da3b3c8216080a0eefb3c01719913782690427fb9ba2ddad98ed8970f4", + "url": "https://files.pythonhosted.org/packages/df/b0/5715b7635e5e25dd26ae32453e784cab59401078aeb3e401027675068583/zope_interface-8.6-cp311-cp311-macosx_10_9_x86_64.whl" } ], "project_name": "zope-interface", @@ -6830,7 +6857,7 @@ "zope.testing; extra == \"testing\"" ], "requires_python": ">=3.10", - "version": "8.5" + "version": "8.6" }, { "artifacts": [ diff --git a/requirements.txt b/requirements.txt index 97fe6cb17b..6355222da6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,77 +7,77 @@ # update the component requirements.txt RandomWords amqp==5.3.1 -apscheduler==3.11.0 -argcomplete==3.6.2 +apscheduler==3.11.3 +argcomplete==3.7.2 backports.zoneinfo[tzdata]; python_version<"3.9" bcrypt==5.0.0 -cffi==1.17.1 -chardet==5.2.0 +cffi==2.1.1 +chardet==7.6.0 ciso8601 -cryptography==43.0.3 -decorator==5.2.1 +cryptography==50.0.1 +decorator==5.3.1 dnspython -editor==1.6.6 -eventlet==0.40.4 -gevent==25.9.1 +editor==1.8.0 +eventlet==0.41.2 +gevent==26.8.0 gitdb==4.0.12 -gitpython==3.1.45 -greenlet==3.2.2 -gunicorn==23.0.0 +gitpython==3.1.61 +greenlet==3.5.5 +gunicorn==25.3.0 importlib-metadata importlib_resources==6.5.2 jinja2==3.1.6 jsonpath-rw==1.4.0 jsonschema==4.26.0 -kombu==5.5.4 +kombu==5.6.2 lockfile==0.12.2 mock==5.2.0 -mongoengine==0.29.1 -networkx==3.1 -openapi-spec-validator==0.7.2 -orjson==3.10.15 +mongoengine==0.29.3 +networkx==3.3 +openapi-spec-validator==0.9.0 +orjson==3.12.0 orquesta@ git+https://github.com/StackStorm/orquesta.git oslo.config==9.6.0 oslo.utils==7.3.0 paramiko==3.5.1 -prettytable==3.11.0 -prompt-toolkit==3.0.52 -psutil==7.1.0 +prettytable==3.18.0 +prompt-toolkit==3.0.53 +psutil==7.2.2 pyOpenSSL<25.2 -pygments==2.19.2 +pygments==2.21.0 pymongo==4.6.3 -pynacl==1.6.0 -pyparsing==3.1.4 +pynacl==1.6.2 +pyparsing==3.3.2 pyrabbit pysocks pytest==7.0.1 python-dateutil==2.9.0.post0 python-json-logger python-statsd==2.1.0 -pytz==2025.2 +pytz==2026.3.post1 pywinrm==0.5.0 pyyaml==6.0.3 -redis==6.1.1 +redis==8.1.0 referencing -requests==2.32.4 +requests==2.34.2 retrying==1.4.2 routes==2.5.1 semver==3.0.4 setuptools==82.0.1 simplejson six==1.17.0 -sseclient-py==1.8.0 +sseclient-py==1.9.0 st2-auth-backend-flat-file st2-auth-backend-pam@ git+https://github.com/StackStorm/st2-auth-backend-pam.git@master st2-auth-ldap@ git+https://github.com/StackStorm/st2-auth-ldap.git@master st2-rbac-backend@ git+https://github.com/StackStorm/st2-rbac-backend.git@master -stevedore==5.3.0 -tenacity==9.0.0 -tooz==6.3.0 +stevedore==5.8.0 +tenacity==9.1.4 +tooz==8.1.0 typing-extensions>=4.13.2; python_version < "3.11" -urllib3==2.2.3 -webob==1.8.9 -webtest==3.0.1 +urllib3==2.7.0 +webob==1.8.11 +webtest==3.0.7 zake==0.2.2 -zipp==3.20.2 -zstandard==0.23.0 +zipp==4.1.0 +zstandard==0.25.0 diff --git a/st2actions/requirements.txt b/st2actions/requirements.txt index d185048c00..897ac8b488 100644 --- a/st2actions/requirements.txt +++ b/st2actions/requirements.txt @@ -5,22 +5,22 @@ # If you want to update depdencies for a single component, modify the # in-requirements.txt for that component and then run 'make requirements' to # update the component requirements.txt -apscheduler==3.11.0 +apscheduler==3.11.3 backports.zoneinfo[tzdata]; python_version<"3.9" -chardet==5.2.0 -eventlet==0.40.4 -gevent==25.9.1 -gitpython==3.1.45 +chardet==7.6.0 +eventlet==0.41.2 +gevent==26.8.0 +gitpython==3.1.61 jinja2==3.1.6 -kombu==5.5.4 +kombu==5.6.2 lockfile==0.12.2 oslo.config==9.6.0 oslo.utils==7.3.0 -pyparsing==3.1.4 +pyparsing==3.3.2 python-dateutil==2.9.0.post0 python-json-logger pyyaml==6.0.3 -requests==2.32.4 +requests==2.34.2 setuptools==82.0.1 six==1.17.0 -urllib3==2.2.3 +urllib3==2.7.0 diff --git a/st2api/requirements.txt b/st2api/requirements.txt index 580dc57d39..0942a0d280 100644 --- a/st2api/requirements.txt +++ b/st2api/requirements.txt @@ -6,15 +6,15 @@ # in-requirements.txt for that component and then run 'make requirements' to # update the component requirements.txt backports.zoneinfo[tzdata]; python_version<"3.9" -eventlet==0.40.4 -gevent==25.9.1 -gunicorn==23.0.0 +eventlet==0.41.2 +gevent==26.8.0 +gunicorn==25.3.0 jsonschema==4.26.0 -kombu==5.5.4 -mongoengine==0.29.1 +kombu==5.6.2 +mongoengine==0.29.3 oslo.config==9.6.0 oslo.utils==7.3.0 pymongo==4.6.3 -pyparsing==3.1.4 +pyparsing==3.3.2 simplejson six==1.17.0 diff --git a/st2auth/requirements.txt b/st2auth/requirements.txt index 4636d5bb55..83789f05fb 100644 --- a/st2auth/requirements.txt +++ b/st2auth/requirements.txt @@ -6,13 +6,13 @@ # in-requirements.txt for that component and then run 'make requirements' to # update the component requirements.txt bcrypt==5.0.0 -eventlet==0.40.4 -gevent==25.9.1 -gunicorn==23.0.0 +eventlet==0.41.2 +gevent==26.8.0 +gunicorn==25.3.0 oslo.config==9.6.0 pymongo==4.6.3 six==1.17.0 st2-auth-backend-flat-file st2-auth-backend-pam@ git+https://github.com/StackStorm/st2-auth-backend-pam.git@master st2-auth-ldap@ git+https://github.com/StackStorm/st2-auth-ldap.git@master -stevedore==5.3.0 +stevedore==5.8.0 diff --git a/st2client/requirements.txt b/st2client/requirements.txt index 6d254bdd4f..0fab590114 100644 --- a/st2client/requirements.txt +++ b/st2client/requirements.txt @@ -5,26 +5,26 @@ # If you want to update depdencies for a single component, modify the # in-requirements.txt for that component and then run 'make requirements' to # update the component requirements.txt -argcomplete==3.6.2 -cffi==1.17.1 -chardet==5.2.0 -cryptography==43.0.3 -editor==1.6.6 +argcomplete==3.7.2 +cffi==2.1.1 +chardet==7.6.0 +cryptography==50.0.1 +editor==1.8.0 importlib-metadata jsonpath-rw==1.4.0 jsonschema==4.26.0 -orjson==3.10.15 -prettytable==3.11.0 -prompt-toolkit==3.0.52 +orjson==3.12.0 +prettytable==3.18.0 +prompt-toolkit==3.0.53 pyOpenSSL<25.2 -pygments==2.19.2 +pygments==2.21.0 pysocks python-dateutil==2.9.0.post0 -pytz==2025.2 +pytz==2026.3.post1 pyyaml==6.0.3 -requests==2.32.4 +requests==2.34.2 six==1.17.0 -sseclient-py==1.8.0 +sseclient-py==1.9.0 typing-extensions>=4.13.2; python_version < "3.11" -urllib3==2.2.3 -zipp==3.20.2 +urllib3==2.7.0 +zipp==4.1.0 diff --git a/st2common/requirements.txt b/st2common/requirements.txt index 1e5fc9af48..529c330635 100644 --- a/st2common/requirements.txt +++ b/st2common/requirements.txt @@ -6,48 +6,48 @@ # in-requirements.txt for that component and then run 'make requirements' to # update the component requirements.txt amqp==5.3.1 -apscheduler==3.11.0 +apscheduler==3.11.3 backports.zoneinfo[tzdata]; python_version<"3.9" -cffi==1.17.1 -chardet==5.2.0 +cffi==2.1.1 +chardet==7.6.0 ciso8601 -cryptography==43.0.3 -decorator==5.2.1 +cryptography==50.0.1 +decorator==5.3.1 dnspython -eventlet==0.40.4 -gevent==25.9.1 +eventlet==0.41.2 +gevent==26.8.0 gitdb==4.0.12 -gitpython==3.1.45 -greenlet==3.2.2 +gitpython==3.1.61 +greenlet==3.5.5 importlib_resources==6.5.2 jinja2==3.1.6 jsonpath-rw==1.4.0 jsonschema==4.26.0 -kombu==5.5.4 +kombu==5.6.2 lockfile==0.12.2 -mongoengine==0.29.1 -networkx==3.1 -openapi-spec-validator==0.7.2 -orjson==3.10.15 +mongoengine==0.29.3 +networkx==3.3 +openapi-spec-validator==0.9.0 +orjson==3.12.0 orquesta@ git+https://github.com/StackStorm/orquesta.git oslo.config==9.6.0 paramiko==3.5.1 pyOpenSSL<25.2 pymongo==4.6.3 -pynacl==1.6.0 +pynacl==1.6.2 python-dateutil==2.9.0.post0 python-statsd==2.1.0 pyyaml==6.0.3 -redis==6.1.1 -requests==2.32.4 +redis==8.1.0 +requests==2.34.2 retrying==1.4.2 routes==2.5.1 semver==3.0.4 six==1.17.0 st2-rbac-backend@ git+https://github.com/StackStorm/st2-rbac-backend.git@master -tenacity==9.0.0 -tooz==6.3.0 -urllib3==2.2.3 -webob==1.8.9 +tenacity==9.1.4 +tooz==8.1.0 +urllib3==2.7.0 +webob==1.8.11 zake==0.2.2 -zstandard==0.23.0 +zstandard==0.25.0 diff --git a/st2reactor/requirements.txt b/st2reactor/requirements.txt index fe0c7fb174..e10bf170f5 100644 --- a/st2reactor/requirements.txt +++ b/st2reactor/requirements.txt @@ -5,13 +5,13 @@ # If you want to update depdencies for a single component, modify the # in-requirements.txt for that component and then run 'make requirements' to # update the component requirements.txt -apscheduler==3.11.0 +apscheduler==3.11.3 backports.zoneinfo[tzdata]; python_version<"3.9" -eventlet==0.40.4 -gevent==25.9.1 +eventlet==0.41.2 +gevent==26.8.0 jsonpath-rw==1.4.0 jsonschema==4.26.0 -kombu==5.5.4 +kombu==5.6.2 oslo.config==9.6.0 python-dateutil==2.9.0.post0 six==1.17.0 diff --git a/st2stream/requirements.txt b/st2stream/requirements.txt index 6cdc4670cf..0276eda776 100644 --- a/st2stream/requirements.txt +++ b/st2stream/requirements.txt @@ -6,14 +6,14 @@ # in-requirements.txt for that component and then run 'make requirements' to # update the component requirements.txt backports.zoneinfo[tzdata]; python_version<"3.9" -eventlet==0.40.4 -gevent==25.9.1 -gunicorn==23.0.0 +eventlet==0.41.2 +gevent==26.8.0 +gunicorn==25.3.0 jsonschema==4.26.0 -kombu==5.5.4 -mongoengine==0.29.1 +kombu==5.6.2 +mongoengine==0.29.3 oslo.config==9.6.0 oslo.utils==7.3.0 pymongo==4.6.3 -pyparsing==3.1.4 +pyparsing==3.3.2 six==1.17.0 diff --git a/st2tests/requirements.txt b/st2tests/requirements.txt index 27f69000ae..467f1ce5b0 100644 --- a/st2tests/requirements.txt +++ b/st2tests/requirements.txt @@ -7,7 +7,7 @@ # update the component requirements.txt RandomWords mock==5.2.0 -psutil==7.1.0 +psutil==7.2.2 pyrabbit pytest==7.0.1 -webtest==3.0.1 +webtest==3.0.7 diff --git a/test-requirements.txt b/test-requirements.txt index a17c5b499c..7ab9cdf9be 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -25,16 +25,16 @@ alabaster<0.7.14 # Required by st2client tests pyyaml==6.0.3 # Constrain pygments required by editor to align with st2 core version -pygments==2.19.2 +pygments==2.21.0 RandomWords -gunicorn==23.0.0 -psutil==7.1.0 -webtest==3.0.1 +gunicorn==25.3.0 +psutil==7.2.2 +webtest==3.0.7 # Bump to latest to meet sphinx requirements. rstcheck==6.2.1 tox==4.14.2 pyrabbit -prance==25.4.8.0 +prance==26.7.19.0 # pip-tools provides pip-compile: to check for version conflicts pip-tools==7.5.3 pytest==7.0.1 @@ -45,8 +45,8 @@ pytest-xdist==2.5.0 # for Makefile-based pytest runs pytest-custom_exit_code # zstandard is used for micro benchmarks -zstandard==0.23.0 +zstandard==0.25.0 # ujson is used for micro benchmarks -ujson==5.10.0 +ujson==5.13.0 # needed by integration tests for coordination -redis==6.1.1 +redis==8.1.0 From 6090f548050104e7f06d386f2277611cbf1b94a0 Mon Sep 17 00:00:00 2001 From: Jacob Zufelt Date: Fri, 28 Aug 2026 15:52:57 -0600 Subject: [PATCH 22/22] give cryptography a range so locfiles_to_reqs respects it --- fixed-requirements.txt | 3 ++- requirements.txt | 2 +- st2client/requirements.txt | 2 +- st2common/requirements.txt | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/fixed-requirements.txt b/fixed-requirements.txt index d582c1e1a5..495efb8e7a 100644 --- a/fixed-requirements.txt +++ b/fixed-requirements.txt @@ -10,7 +10,8 @@ amqp==5.3.1 apscheduler==3.11.3 chardet==7.6.0 cffi==2.1.1 -cryptography==50.0.1 +# range (not ==) so lockfiles_to_reqs.py won't auto-bump past pyOpenSSL's cryptography<46 ceiling +cryptography>=41.0.5,<46 eventlet==0.41.2 # Note: installs gitpython==3.1.37 (security fixed) under py3.8 and gitpython==3.1.18 (latest available, vulnerable) under py3.6 gitpython==3.1.61 diff --git a/requirements.txt b/requirements.txt index 6355222da6..c718839869 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ bcrypt==5.0.0 cffi==2.1.1 chardet==7.6.0 ciso8601 -cryptography==50.0.1 +cryptography>=41.0.5,<46 decorator==5.3.1 dnspython editor==1.8.0 diff --git a/st2client/requirements.txt b/st2client/requirements.txt index 0fab590114..c4344c2393 100644 --- a/st2client/requirements.txt +++ b/st2client/requirements.txt @@ -8,7 +8,7 @@ argcomplete==3.7.2 cffi==2.1.1 chardet==7.6.0 -cryptography==50.0.1 +cryptography>=41.0.5,<46 editor==1.8.0 importlib-metadata jsonpath-rw==1.4.0 diff --git a/st2common/requirements.txt b/st2common/requirements.txt index 529c330635..803ac5b7ba 100644 --- a/st2common/requirements.txt +++ b/st2common/requirements.txt @@ -11,7 +11,7 @@ backports.zoneinfo[tzdata]; python_version<"3.9" cffi==2.1.1 chardet==7.6.0 ciso8601 -cryptography==50.0.1 +cryptography>=41.0.5,<46 decorator==5.3.1 dnspython eventlet==0.41.2