diff --git a/openstack_requirements/check.py b/openstack_requirements/check.py index c6a5d6637..e3d877272 100644 --- a/openstack_requirements/check.py +++ b/openstack_requirements/check.py @@ -18,12 +18,9 @@ import re import sys -from packaging import markers - from openstack_requirements.project import Project from openstack_requirements import requirement -MIN_PY_VERSION = '3.5' PY3_GLOBAL_SPECIFIER_RE = re.compile( r'python_version(==|>=|>)[\'"]3\.\d+[\'"]' ) @@ -36,14 +33,10 @@ class RequirementsList: def __init__(self, name: str, project: Project) -> None: self.name = name self.reqs_by_file: dict[str, dict[str, set[str]]] = {} + self.optional_reqs_by_file: dict[str, dict[str, set[str]]] = {} self.project = project self.failed = False - @property - def reqs(self) -> dict[str, set[str]]: - """Flattens the list of per-file reqs.""" - return {k: v for d in self.reqs_by_file.values() for k, v in d.items()} - def extract_reqs( self, content: list[str], strict: bool ) -> dict[str, set[str]]: @@ -112,7 +105,7 @@ def process(self, strict: bool = True) -> None: print(f"Processing {fname} (extras)") for name, content in extras.items(): print(f" Processing {name!r} extra") - self.reqs_by_file[f'{fname} ({name!r} extra)'] = ( + self.optional_reqs_by_file[f'{fname} ({name!r} extra)'] = ( self.extract_reqs(content, strict) ) @@ -120,9 +113,9 @@ def process(self, strict: bool = True) -> None: print(f"Processing {fname} (dependency-groups)") for name, content in groups.items(): print(f" Processing {name!r} dependency group") - self.reqs_by_file[f'{fname} ({name!r} dependency group)'] = ( - self.extract_reqs(content, strict) - ) + self.optional_reqs_by_file[ + f'{fname} ({name!r} dependency group)' + ] = self.extract_reqs(content, strict) def _get_exclusions(req): @@ -234,29 +227,14 @@ def get_global_reqs(content): return global_reqs -def _get_python3_reqs(reqs): - """Filters out the reqs that are less than our minimum version.""" - results = [] - for req in reqs: - if not req.markers: - results.append(req) - else: - req_markers = markers.Marker(req.markers) - if req_markers.evaluate( - { - 'python_version': MIN_PY_VERSION, - } - ): - results.append(req) - return results - - def _validate_one( name, reqs, denylist, global_reqs, backports, + *, + is_optional, ): """Returns True if there is a failure.""" @@ -267,6 +245,9 @@ def _validate_one( return False if name not in global_reqs: + if is_optional: + return False + print(f"ERROR: Requirement '{reqs}' not in openstack/requirements") return True @@ -319,18 +300,26 @@ def validate( failed = False # iterate through the changing entries and see if they match the global # equivalents we want enforced - for fname, freqs in head_reqs.reqs_by_file.items(): - print(f"Validating {fname}") - for name, reqs in freqs.items(): - failed = ( - _validate_one( - name, - reqs, - denylist, - global_reqs, - backports, + # note that extras and dependency groups are project-specific and may not + # be present in global-requirements, so we only note their absence rather + # than failing if so + for reqs_by_file, is_optional in ( + (head_reqs.reqs_by_file, False), + (head_reqs.optional_reqs_by_file, True), + ): + for fname, freqs in reqs_by_file.items(): + print(f"Validating {fname}") + for name, reqs in freqs.items(): + failed = ( + _validate_one( + name, + reqs, + denylist, + global_reqs, + backports, + is_optional=is_optional, + ) + or failed ) - or failed - ) return failed diff --git a/openstack_requirements/project.py b/openstack_requirements/project.py index 6924a9c08..3676c9e0d 100644 --- a/openstack_requirements/project.py +++ b/openstack_requirements/project.py @@ -164,10 +164,14 @@ def verify_pyproject_toml(root: str) -> bool: return False if 'build-system' not in data: - print("pyproject.toml is missing 'build-system' table", file=sys.stderr) + print( + "pyproject.toml is missing 'build-system' table", file=sys.stderr + ) return False - if (build_backend := data['build-system'].get('build-backend')) != 'pbr.build': + if ( + build_backend := data['build-system'].get('build-backend') + ) != 'pbr.build': print( f"pyproject.toml has invalid 'build-system.build-backend'. " f"Expected 'pbr.build'; got {build_backend!r}", diff --git a/openstack_requirements/tests/test_check.py b/openstack_requirements/tests/test_check.py index 09b28a581..881228323 100644 --- a/openstack_requirements/tests/test_check.py +++ b/openstack_requirements/tests/test_check.py @@ -44,11 +44,15 @@ def test_extras__setup_cfg(self): req_list = check.RequirementsList('test-project', project_data) req_list.process(strict=False) - self.assertIn("setup.cfg ('test' extra)", req_list.reqs_by_file) - self.assertIn("setup.cfg ('dev' extra)", req_list.reqs_by_file) + self.assertIn( + "setup.cfg ('test' extra)", req_list.optional_reqs_by_file + ) + self.assertIn( + "setup.cfg ('dev' extra)", req_list.optional_reqs_by_file + ) - test_reqs = req_list.reqs_by_file["setup.cfg ('test' extra)"] - dev_reqs = req_list.reqs_by_file["setup.cfg ('dev' extra)"] + test_reqs = req_list.optional_reqs_by_file["setup.cfg ('test' extra)"] + dev_reqs = req_list.optional_reqs_by_file["setup.cfg ('dev' extra)"] self.assertEqual(2, len(test_reqs)) self.assertIn('pytest', test_reqs) @@ -88,30 +92,33 @@ def test_extras__pyproject_toml(self): self.assertIn('pyproject.toml (dependencies)', req_list.reqs_by_file) self.assertIn( - "pyproject.toml ('security' extra)", req_list.reqs_by_file + "pyproject.toml ('security' extra)", + req_list.optional_reqs_by_file, ) self.assertIn( - "pyproject.toml ('test' dependency group)", req_list.reqs_by_file + "pyproject.toml ('test' dependency group)", + req_list.optional_reqs_by_file, ) self.assertIn( - "pyproject.toml ('typing' dependency group)", req_list.reqs_by_file + "pyproject.toml ('typing' dependency group)", + req_list.optional_reqs_by_file, ) self.assertIn( "pyproject.toml ('typing-test' dependency group)", - req_list.reqs_by_file, + req_list.optional_reqs_by_file, ) main_reqs = req_list.reqs_by_file['pyproject.toml (dependencies)'] - security_reqs = req_list.reqs_by_file[ + security_reqs = req_list.optional_reqs_by_file[ "pyproject.toml ('security' extra)" ] - test_reqs = req_list.reqs_by_file[ + test_reqs = req_list.optional_reqs_by_file[ "pyproject.toml ('test' dependency group)" ] - typing_reqs = req_list.reqs_by_file[ + typing_reqs = req_list.optional_reqs_by_file[ "pyproject.toml ('typing' dependency group)" ] - typing_test_reqs = req_list.reqs_by_file[ + typing_test_reqs = req_list.optional_reqs_by_file[ "pyproject.toml ('typing-test' dependency group)" ] @@ -347,6 +354,7 @@ def test_unchanged(self): denylist=requirement.parse(''), backports=self.backports, global_reqs=global_reqs, + is_optional=False, ) ) @@ -361,6 +369,7 @@ def test_denylisted(self): denylist=requirement.parse('name'), backports=self.backports, global_reqs=global_reqs, + is_optional=False, ) ) @@ -376,6 +385,7 @@ def test_denylisted_mismatch(self): denylist=requirement.parse('name'), backports=self.backports, global_reqs=global_reqs, + is_optional=False, ) ) @@ -390,6 +400,7 @@ def test_not_in_global_list(self): denylist=requirement.parse(''), backports=self.backports, global_reqs=global_reqs, + is_optional=False, ) ) @@ -404,6 +415,7 @@ def test_new_item_matches_global_list(self): denylist=requirement.parse(''), backports=self.backports, global_reqs=global_reqs, + is_optional=False, ) ) @@ -419,6 +431,7 @@ def test_new_item_lower_min(self): denylist=requirement.parse(''), backports=self.backports, global_reqs=global_reqs, + is_optional=False, ) ) @@ -436,6 +449,7 @@ def test_new_item_extra_exclusion(self): denylist=requirement.parse(''), backports=self.backports, global_reqs=global_reqs, + is_optional=False, ) ) @@ -451,6 +465,7 @@ def test_new_item_missing_exclusion(self): denylist=requirement.parse(''), backports=self.backports, global_reqs=global_reqs, + is_optional=False, ) ) @@ -476,6 +491,7 @@ def test_new_item_matches_global_list_with_extra(self): denylist=requirement.parse(''), backports=self.backports, global_reqs=global_reqs, + is_optional=False, ) ) @@ -500,6 +516,7 @@ def test_new_item_missing_extra_line(self): denylist=requirement.parse(''), backports=self.backports, global_reqs=global_reqs, + is_optional=False, ) ) @@ -525,6 +542,73 @@ def test_new_item_mismatches_global_list_with_extra(self): denylist=requirement.parse(''), backports=self.backports, global_reqs=global_reqs, + is_optional=False, + ) + ) + + def test_optional_not_in_global_list(self): + # If an optional package is not in the global list, that is not an + # error. + reqs = [r for r, line in requirement.parse('name>=1.2,!=1.4')['name']] + global_reqs = check.get_global_reqs('') + self.assertFalse( + check._validate_one( + 'name', + reqs=reqs, + denylist=requirement.parse(''), + backports=self.backports, + global_reqs=global_reqs, + is_optional=True, + ) + ) + + def test_optional_matches_global_list(self): + # If an optional package is in the global list and matches, + # everything is OK. + reqs = [r for r, line in requirement.parse('name>=1.2,!=1.4')['name']] + global_reqs = check.get_global_reqs('name>=1.2,!=1.4') + self.assertFalse( + check._validate_one( + 'name', + reqs=reqs, + denylist=requirement.parse(''), + backports=self.backports, + global_reqs=global_reqs, + is_optional=True, + ) + ) + + def test_optional_mismatches_global_list(self): + # If an optional package is in the global list and does not match, + # that is an error. + reqs = [ + r for r, line in requirement.parse('name>=1.2,!=1.4,!=1.5')['name'] + ] + global_reqs = check.get_global_reqs('name>=1.2,!=1.4') + self.assertTrue( + check._validate_one( + 'name', + reqs=reqs, + denylist=requirement.parse(''), + backports=self.backports, + global_reqs=global_reqs, + is_optional=True, + ) + ) + + def test_optional_denylisted(self): + # If the optional package is denylisted, everything is OK even if absent + # from global-requirements. + reqs = [r for r, line in requirement.parse('name>=1.2,!=1.4')['name']] + global_reqs = check.get_global_reqs('') + self.assertFalse( + check._validate_one( + 'name', + reqs=reqs, + denylist=requirement.parse('name'), + backports=self.backports, + global_reqs=global_reqs, + is_optional=True, ) ) diff --git a/upper-constraints.txt b/upper-constraints.txt index 757449101..24ba51218 100644 --- a/upper-constraints.txt +++ b/upper-constraints.txt @@ -8,15 +8,15 @@ voluptuous===0.16.0 chardet===6.0.0.post1 enum-compat===0.0.3 -netmiko===4.7.0 +netmiko===4.8.0 sshtunnel===0.4.0 PasteDeploy===3.1.0 Routes===2.5.1 rtslib-fb===2.2.4 oslo.limit===2.12.0 restructuredtext_lint===2.0.2 -yarl===1.24.5 -tzdata===2026.3 +yarl===1.25.1 +tzdata===2026.4 smmap===5.0.3 confget===5.1.2 XStatic-Angular-Bootstrap===2.5.0.1 @@ -24,34 +24,34 @@ WebOb===1.8.11 sphinxcontrib-actdiag===3.0.0 pecan===1.8.0 os-api-ref===3.2.0 -python-ldap===3.4.7 +python-ldap===3.4.8 oslo.concurrency===7.6.1 -websocket-client===1.9.0 +websocket-client===1.9.2 osprofiler===4.4.0 os-resource-classes===1.1.0 mypy_extensions===1.1.0 tabulate===0.10.0 python-ironic-inspector-client===5.4.0 -lxml===6.1.1 +lxml===6.1.3 vintage===0.4.1 rst2txt===1.1.0 setproctitle===1.3.7 pytest===9.1.1 a2wsgi===1.10.10 -python-slugify===8.0.4 +python-slugify===9.1.1 cursive===0.3.1 oslo.service===4.8.0 django-appconf===1.2.0 -ntc_templates===9.2.0 +ntc_templates===9.3.0 sphinxcontrib-nwdiag===2.0.0 rbd-iscsi-client===0.1.8 alabaster===1.0.0 -multidict===6.7.1 +multidict===6.9.1 pbr===7.0.3 munch===4.0.0 waiting===1.5.0 attrs===26.1.0 -jwcrypto===1.5.8 +jwcrypto===1.6.1 Pint===0.25.3 oslo.i18n===6.9.0 jsonpath-rw-ext===1.2.2 @@ -59,82 +59,90 @@ python-mistralclient===6.3.0 oslo.context===6.5.0 rcssmin===1.2.2 pycadf===4.1.0 -grpcio===1.83.0 +grpcio===1.84.0 sniffio===1.3.1 fixtures===4.3.2 neutron-lib===5.0.1 XStatic-FileSaver===1.3.2.1 jaraco.functools===4.6.0 +annotated-doc===0.0.5 +jaraco.text===4.3.0 oslo.metrics===0.17.0 storage-interfaces===1.0.5 -pydantic===2.13.4 +pydantic===2.13.5 pystache===0.6.8 XStatic-Font-Awesome===6.2.1.2 aiohttp===3.14.3 waitress===3.0.2 os-refresh-config===14.0.1 -pysnmp===7.1.28 -Mako===1.4.1 +pysnmp===7.1.29 +Mako===1.4.3 +vultr===1.0.1 sphinxcontrib-htmlhelp===2.1.0 XStatic-jQuery===3.7.1.1 sphinx-copybutton===0.5.2 beartype===0.22.9 +jaraco.collections===5.2.1 ddt===1.7.2 pyserial===3.5 -moto===5.2.2 +moto===5.2.3 infi.dtypes.wwn===0.2.0 -awscrt===0.36.2 +awscrt===0.37.0 pcre2===0.7.1 python-freezerclient===6.5.0 -py-pure-client===1.93.0 +py-pure-client===1.94.2 krest===1.3.8 -psycopg2===2.9.12 +psycopg2===2.9.13 networkx===3.6.1 cheroot===11.1.2 XStatic-Angular===1.8.2.3 zuul-sphinx===0.8.1 ply===3.11 -google-api-core===2.34.0 +google-api-core===2.39.0 requests-toolbelt===1.0.0 -simplejson===4.1.1 +simplejson===4.1.2 python-swiftclient===4.11.0 -pyOpenSSL===24.2.1 +shellingham===1.5.4 +pyOpenSSL===26.4.0 typing-inspection===0.4.4 monasca-common===3.8.0 hyperframe===6.1.0 -zeroconf===0.150.0 +zeroconf===0.151.3 scipy===1.17.1 -opentelemetry-exporter-otlp===1.44.0 +opentelemetry-exporter-otlp===1.45.0 +python-gnupg===0.5.6 rsd-lib===1.2.0 XStatic-Jasmine===2.4.1.3 -googleapis-common-protos===1.75.1 +googleapis-common-protos===1.75.4 python-glanceclient===4.13.0 prometheus_client===0.26.0 jaraco.classes===3.4.0 +typer-slim===0.24.0 debtcollector===3.1.0 -responses===0.26.2 +responses===0.26.3 prompt_toolkit===3.0.53 croniter===6.2.4 horizon===27.0.0 octavia-lib===3.12.0 python-watcherclient===4.11.0 MarkupSafe===3.0.3 +cloudpickle===3.1.2 doc8===2.0.0 -pymongo===4.17.0 +pymongo===4.18.2 python-cloudkittyclient===6.2.0 -soupsieve===2.9.2 +soupsieve===2.10 sqlparse===0.6.0 oslotest===6.1.1 jsonpointer===3.1.1 defusedxml===0.7.1 -opentelemetry-sdk===1.44.0 +opentelemetry-sdk===1.45.0 netaddr===1.3.0 pyghmi===1.6.19 sphinxcontrib-blockdiag===3.0.0 aiosqlite===0.22.1 priority===2.0.0 gnocchiclient===7.2.0 -wcwidth===0.8.2 +wcwidth===0.9.1 sphinxcontrib.datatemplates===0.11.0 jsonpath-rw===1.4.0 prettytable===3.18.0 @@ -143,14 +151,14 @@ pathspec===1.1.1 taskflow===6.4.0 arrow===1.4.0 semantic-version===2.10.0 -ConfigArgParse===1.7.5 +ConfigArgParse===1.7.7 async-timeout===5.0.1 virtualbmc===3.3.0 -SQLAlchemy===2.0.52 +SQLAlchemy===2.0.54 pyroute2===0.8.1 -google-auth===2.56.3 +google-auth===2.58.1 kazoo===2.11.0 -pyspnego===0.12.1 +pyspnego===0.12.2 trio-websocket===0.12.2 XStatic-roboto-fontface===0.8.0.1 pyudev===0.24.4 @@ -158,41 +166,45 @@ eventlet===0.41.2 openstack-doc-tools===4.0.3 oslo.messaging===18.3.0 jira===3.10.5 -PyJWT===2.13.0 +PyJWT===2.15.0 typing_extensions===4.16.0 zVMCloudConnector===1.6.3 -paramiko===4.0.0 +paramiko===5.0.0 ifaddr===0.2.0 reno===4.1.0 ncclient===0.7.1 -imagesize===2.0.0 +imagesize===2.0.1 pydot===4.0.1 -urllib3===2.7.0 +urllib3===2.8.0 graphviz===0.21 -PyKMIP===0.10.0 +portend===3.2.1 +PyKMIP===0.11.0 python-observabilityclient===1.4.0 whereto===0.6.0 networking-generic-switch===10.1.0 -pywbem===1.9.0 +pywbem===1.9.1 python-subunit===1.4.6 +tornado===6.5.10 pycparser===3.0 mock===5.2.0 PyYAML===6.0.3 beautifulsoup4===4.15.0 -ovs===3.7.1 -cryptography===43.0.3 +ovs===4.0.0 +cryptography===50.0.1 httpcore===1.0.9 URLObject===3.0.0 -psycopg2-binary===2.9.12 +psycopg2-binary===2.9.13 glance_store===5.7.0 openstack-release-test===8.5.0 +pycryptodomex===3.23.0 requests-mock===1.12.1 os-apply-config===14.0.1 -gunicorn===26.0.0 +xxhash===4.0.1 +gunicorn===26.2.0 storpool===7.3.0 textfsm===2.1.0 python-3parclient===4.4 -libvirt-python===12.6.0 +libvirt-python===12.7.0 python-zunclient===5.5.0 tzlocal===5.4.4 sysv_ipc===1.2.0 @@ -207,7 +219,7 @@ os-client-config===2.3.0 XStatic-Angular-Gettext===2.4.1.1 Deprecated===1.3.1 h11===0.16.0 -Pygments===2.20.0 +Pygments===2.21.0 XStatic-Hogan===2.0.0.5 api_object_schema===2.0.0 XStatic-objectpath===1.2.1.1 @@ -219,10 +231,11 @@ Jinja2===3.1.6 XStatic-Bootstrap-SCSS===3.4.1.1 pyzabbix===1.3.1 ptyprocess===0.7.0 -amqp===5.3.1 +amqp===5.4.0 ruamel.yaml===0.19.1 websockify===0.13.0 -gssapi===1.11.1 +CherryPy===18.10.0 +gssapi===1.12.0 XStatic-JQuery.quicksearch===2.0.3.3 pyasn1_modules===0.4.2 mpmath===1.3.0 @@ -233,8 +246,9 @@ sphinx-feature-classification===2.1.0 XStatic-JQuery-Migrate===3.3.2.2 pytest-html===4.2.0 appdirs===1.4.4 -google-auth-httplib2===0.4.1 +google-auth-httplib2===0.4.2 daiquiri===3.4.0 +apache-libcloud===3.9.1 influxdb===5.3.2 funcparserlib===2.0.0a0 passlib===1.7.4 @@ -243,39 +257,42 @@ os-brick===7.1.1 valkey===6.1.1 scp===0.16.1 lark===1.3.1 +opentelemetry-exporter-prometheus===0.66b0 python-zaqarclient===4.7.0 ldappool===3.0.0 hpack===4.2.0 -joblib===1.5.3 +joblib===1.6.0 roman-numerals===4.1.0 -google-api-python-client===2.198.0 +google-api-python-client===2.200.0 castellan===5.8.0 oslo.versionedobjects===3.12.0 enmerkar===0.7.1 +typer===0.27.2 webcolors===25.10.0 aodhclient===3.12.0 autobahn===26.7.1 +truststore===0.10.4 SQLAlchemy-Utils===0.42.1 retryz===0.1.9 pluggy===1.6.0 -coverage===7.15.4 +coverage===7.16.1 pyee===13.0.1 freezegun===1.5.5 mdurl===0.1.2 toml===0.10.2 -pycdlib===1.20.0 +pycdlib===1.21.0 pyperclip===1.11.0 cassandra-driver===3.30.1 XStatic-Angular-Schema-Form===0.8.13.1 -opentelemetry-exporter-otlp-proto-http===1.44.0 -gabbi===4.2.0 +opentelemetry-exporter-otlp-proto-http===1.45.0 +gabbi===4.2.3 nwdiag===3.0.0 XStatic-bootswatch===3.3.7.1 annotated-types===0.8.0 pytest-xdist===3.8.0 XStatic-JS-Yaml===3.13.1.2 XStatic-term.js===0.0.7.1 -oslo.log===8.3.1 +oslo.log===8.3.2 nodeenv===1.10.0 gossip===2.5.0 suds-community===1.2.0 @@ -283,13 +300,13 @@ os_vif===5.2.1 qrcode===8.2 oslo.middleware===8.2.0 XStatic-mdi===1.6.50.3 -pydantic_core===2.46.4 +pydantic_core===2.46.5 uritemplate===4.2.0 docutils===0.21.2 -threadpoolctl===3.6.0 +threadpoolctl===3.7.0 os-ken===4.2.2 -ujson===5.13.0 -selenium===4.47.0 +ujson===6.0.0 +selenium===4.49.0 pytest-subtests===0.15.0 mistral-lib===3.6.0 dogtag-pki===11.2.1 @@ -298,11 +315,11 @@ dfs_sdk===1.2.27 sphinxcontrib-seqdiag===3.0.0 os-win===5.9.0 capacity===1.3.14 -playwright===1.62.0 +playwright===1.63.0 markdown-it-py===4.2.0 retrying===1.4.2 -python-discovery===1.5.2 -platformdirs===4.11.3 +python-discovery===1.6.1 +platformdirs===4.11.15 pydotplus===2.0.2 boto3===1.35.99 jeepney===0.9.0 @@ -311,7 +328,7 @@ pillow===12.3.0 infoblox-client===0.6.2 oslo.serialization===5.11.0 warlock===2.1.0 -exabgp===5.0.10 +exabgp===5.0.13 aiomysql===0.3.2 sphinxcontrib-httpdomain===2.0.0 metalsmith===2.5.0 @@ -322,7 +339,7 @@ oslo.vmware===4.10.1 autopage===0.6.0 gitdb===4.0.12 python-monascaclient===2.8.0 -opentelemetry-api===1.44.0 +opentelemetry-api===1.45.0 frozenlist===1.8.0 automaton===3.5.0 os-service-types===1.9.0 @@ -330,17 +347,17 @@ keyring===25.7.0 elementpath===4.8.0 wsgi_intercept===1.13.1 jsonschema-specifications===2025.9.1 -testscenarios===0.6.2 +testscenarios===0.7.0 sphinxcontrib-pecanwsme===0.11.0 sadisplay===0.4.9 enum34===1.1.10 infinisdk===289.1.4 rich-argparse===1.8.0 packaging===26.3 -opentelemetry-exporter-otlp-proto-grpc===1.44.0 +opentelemetry-exporter-otlp-proto-grpc===1.45.0 psutil===7.2.2 txaio===26.6.1 -elasticsearch===9.5.0 +elasticsearch===9.5.1 asgiref===3.12.1 XStatic-JQuery.TableSorter===2.14.5.3 pifpaf===3.4.0 @@ -361,19 +378,19 @@ SecretStorage===3.5.0 XStatic-Rickshaw===1.5.1.3 iso8601===2.1.0 tooz===9.1.0 -idna===3.18 +idna===3.20 Hypercorn===0.18.0 yamlloader===1.6.0 -protobuf===7.35.1 +protobuf===7.36.2 sushy===5.13.0 python-neutronclient===14.0.0 pika===1.4.4 oslo.cache===4.3.0 WebTest===3.0.7 os-collect-config===14.0.1 -edgegrid-python===2.0.7 +edgegrid-python===2.0.8 python-octaviaclient===3.15.0 -pysaml2===7.5.4 +pysaml2===7.5.5 requests-oauthlib===2.0.0 oslo.reports===3.9.0 bitmath===2.1.1 @@ -385,8 +402,9 @@ PyNaCl===1.6.2 osc-lib===4.7.0 more-itertools===11.1.0 seqdiag===3.0.0 +zc.lockfile===4.0 numpy===2.4.6 -msgpack===1.2.1 +msgpack===1.2.2 Sphinx===9.0.4 oslo.config===10.7.0 openstackdocstheme===3.6.0 @@ -395,27 +413,29 @@ rpds-py===2026.6.3 zake===0.2.2 flux===1.4.0 flexparser===0.4 -krb5===0.9.0 -PyMySQL===1.2.0 +krb5===0.10.0 +PyMySQL===1.2.3 uhashring===2.5 kubernetes===36.0.3 httplib2===0.32.0 +opentelemetry-exporter-otlp-common===0.66b0 betamax===0.9.0 construct===2.10.70 pytest-metadata===3.1.1 pyparsing===3.3.2 geomet===1.1.0 -opentelemetry-exporter-otlp-proto-common===1.44.0 +opentelemetry-exporter-otlp-proto-common===1.45.0 distlib===0.4.3 -ast_serialize===0.8.0 +ast_serialize===0.11.2 dogpile.cache===1.5.0 python-barbicanclient===7.6.0 salt===3008.0 -opentelemetry-semantic-conventions===0.65b0 +opentelemetry-semantic-conventions===0.66b0 blinker===1.9.0 WSME===0.13.0 oslo.upgradecheck===2.8.0 sherlock===0.4.1 +tempora===5.12.0 stevedore===5.9.1 botocore===1.35.99 xmltodict===1.0.4 @@ -424,12 +444,12 @@ oslo.rootwrap===7.10.0 Django===5.2.17 pexpect===4.9.0 elastic-transport===9.4.2 -cmd2===4.2.0 -python-json-logger===4.1.0 +cmd2===4.2.4 +python-json-logger===4.2.0 redis===8.1.0 jmespath===1.1.0 cbor2===6.1.4 -click===8.4.2 +click===8.5.0 XStatic-smart-table===1.4.13.3 kuryr-lib===3.5.0 jsonpatch===1.33 @@ -442,35 +462,39 @@ systemd-python===235 python-memcached===1.62 openstacksdk===4.20.0 infi.dtypes.nqn===0.1.0 +looseversion===1.3.0 six===1.17.0 h2===4.4.1 -dulwich===1.2.12 +dulwich===1.2.15 sentinels===1.1.1 kombu===5.6.2 distro===1.9.0 zstd===1.5.7.2 yaql===3.2.0 -durationpy===0.10 +durationpy===0.11 requestsexceptions===1.4.0 testresources===2.1.2 falcon===4.3.1 tomlkit===0.15.1 +opentelemetry-exporter-http-transport===0.66b0 etcd3gw===2.7.0 +timelib===0.3.0 +rpm_vercmp===0.1.2 Flask-RESTful===0.3.10 -GitPython===3.1.59 +GitPython===3.1.62 python-ironicclient===6.3.0 babel===2.18.0 XStatic===1.0.3 XStatic-Angular-FileUpload===12.2.13.2 python-openstackclient===10.3.0 -pyzmq===27.1.0 +pyzmq===27.2.0 oslo.db===18.1.1 simplegeneric===0.8.1 yappi===1.7.6 mbstrdecoder===1.1.5 wsproto===1.3.2 pymemcache===4.0.0 -wrapt===2.3.0 +wrapt===2.4.1 PySocks===1.7.1 oslo.privsep===3.12.0 sphinxcontrib-apidoc===0.6.0 @@ -499,28 +523,28 @@ hpe-storage-flowkit-py===0.9.5 cachez===0.1.2 XStatic-Bootstrap-Datepicker===1.4.0.1 netifaces===0.11.0 -propcache===0.5.2 -cachetools===7.1.7 +propcache===0.5.4 +cachetools===7.2.0 flexcache===0.3 sphinxcontrib-qthelp===2.0.0 keystoneauth1===5.17.0 statsd===4.0.1 -proto-plus===1.28.3 +proto-plus===1.28.4 python-keystoneclient===6.0.0 -diskimage-builder===3.42.0 +diskimage-builder===3.43.0 py-consul===1.7.1 heat-translator===3.5.0 python-magnumclient===5.0.0 docker===7.2.0 repoze-lru===0.8 storops===1.2.12 -anyio===4.14.2 +anyio===4.15.1 aiosignal===1.4.0 XStatic-Angular-lrdragndrop===1.0.2.7 ovsdbapp===2.19.0 aniso8601===10.0.1 rjsmin===1.2.5 -icalendar===7.2.2 +icalendar===7.3.0 configparser===7.2.0 decorator===5.3.1 DateTimeRange===2.3.2 @@ -530,36 +554,36 @@ futurist===3.5.0 jsonschema===4.26.0 sphinxcontrib-devhelp===2.0.0 python-blazarclient===4.6.0 -alembic===1.19.1 +alembic===1.20.0 execnet===2.1.2 sphinxcontrib-programoutput===0.20 storpool.spopenstack===3.2.0 dnspython===2.8.0 oauthlib===3.3.1 zipp===4.1.0 -greenlet===3.5.5 +greenlet===3.5.6 XStatic-Angular-Vis===4.16.0.1 iniconfig===2.3.0 referencing===0.37.0 -confluent-kafka===2.15.0 +confluent-kafka===2.15.1 backports.tarfile===1.2.0 -narwhals===2.24.0 -xvfbwrapper===0.2.29 +narwhals===2.26.0 +xvfbwrapper===0.2.34 influxdb-client===1.50.0 -tosca-parser===2.14.0 +tosca-parser===2.15.0 python-consul2===0.1.5 -charset-normalizer===3.5.0 +charset-normalizer===3.5.1 Flask===3.1.3 httpx===0.28.1 sqlalchemy-filters===0.13.0 sphinxcontrib-runcmd===0.2.0 confspirator===0.3.0 fasteners===0.20 -importlib_metadata===9.0.0 +importlib_metadata===9.0.1 sortedcontainers===2.4.0 microversion_parse===2.1.0 -python-linstor===1.29.0 -filelock===3.32.3 +python-linstor===1.29.1 +filelock===4.0.3 python-tackerclient===2.6.0 python-heatclient===5.3.0 oslo.utils===10.2.0 @@ -568,14 +592,14 @@ itsdangerous===2.2.0 XStatic-jquery-ui===1.13.0.2 monasca-statsd===2.7.0 python-dateutil===2.9.0.post0 -virtualenv===21.7.4 +virtualenv===21.12.1 colorama===0.4.6 confetti===2.5.3 ironic-lib===7.0.0 aiohappyeyeballs===2.7.1 -pytz===2026.3.post1 -opentelemetry-proto===1.44.0 +pytz===2026.4 +opentelemetry-proto===1.45.0 XStatic-D3===3.5.17.1 actdiag===3.0.0 sphinxcontrib-applehelp===2.0.0 -scikit-learn===1.9.0 +scikit-learn===1.9.1