Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
416dba9
Remove logic around Python 3-only branches
stephenfin May 19, 2026
df8c1d8
update constraint for os-traits to new release 3.7.0
Jun 8, 2026
05c8c10
update constraint for etcd3gw to new release 2.7.0
Jun 9, 2026
254e753
update constraint for openstacksdk to new release 4.15.0
Jun 9, 2026
2b39237
update constraint for oslo.utils to new release 10.1.1
Jun 9, 2026
9faec19
update constraint for os-traits to new release 3.8.0
Jun 9, 2026
be2c2f8
Merge "update constraint for etcd3gw to new release 2.7.0"
Jun 9, 2026
bc78431
Merge "update constraint for os-traits to new release 3.8.0"
Jun 10, 2026
f98f5d0
update constraint for python-openstackclient to new release 10.1.0
Jun 10, 2026
3d4eef1
update constraint for oslo.config to new release 10.5.0
Jun 11, 2026
910f540
Merge "update constraint for oslo.config to new release 10.5.0"
Jun 11, 2026
103aea8
Merge "update constraint for openstacksdk to new release 4.15.0"
Jun 11, 2026
68f087c
Merge "Remove logic around Python 3-only branches"
Jun 11, 2026
e198674
Insist on minimal pyproject.toml file
stephenfin May 19, 2026
eba28fa
update constraint for os-ken to new release 4.2.1
Jun 12, 2026
3c52a72
Merge "Insist on minimal pyproject.toml file"
Jun 12, 2026
b83fbd7
Updated from generate-constraints
Jun 13, 2026
1cef312
update constraint for neutron-lib to new release 4.1.0
Jun 17, 2026
8bd62aa
update constraint for python-freezerclient to new release 6.4.0
Jun 17, 2026
72d979f
update constraint for openstacksdk to new release 4.16.0
Jun 17, 2026
fb59852
Merge "update constraint for python-freezerclient to new release 6.4.0"
Jun 17, 2026
4bb8ff9
Merge "update constraint for openstacksdk to new release 4.16.0"
Jun 17, 2026
afc42e2
Add hypercorn
rm-you Jun 16, 2026
f84d342
Merge "Add hypercorn"
Jun 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions global-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ grpcio # Apache-2.0
gunicorn # MIT
httplib2 # MIT
httpx # BSD
Hypercorn # MIT
hvac # Apache-2.0
icalendar # BSD
# Do not make importlib-metadata conditional on Python version: we depend on
Expand Down
25 changes: 3 additions & 22 deletions openstack_requirements/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ def _is_requirement_in_global_reqs(
local_req,
global_reqs,
backports,
allow_3_only=False,
):
req_exclusions = _get_exclusions(local_req)
for global_req in global_reqs:
Expand All @@ -138,10 +137,10 @@ def _is_requirement_in_global_reqs(
local_req_val = getattr(local_req, aname)
global_req_val = getattr(global_req, aname)
if local_req_val != global_req_val:
# if a python 3 version is not spefied in only one of
# if a python 3 version is not specified in only one of
# global requirements or local requirements, allow it since
# python 3-only is okay
if allow_3_only and matching and aname == 'markers':
if matching and aname == 'markers':
if not local_req_val and PY3_GLOBAL_SPECIFIER_RE.match(
global_req_val
):
Expand Down Expand Up @@ -250,7 +249,6 @@ def _validate_one(
denylist,
global_reqs,
backports,
allow_3_only=False,
):
"""Returns True if there is a failure."""

Expand All @@ -276,7 +274,6 @@ def _validate_one(
req,
global_reqs[name],
backports,
allow_3_only,
):
return True

Expand All @@ -289,22 +286,8 @@ def _validate_one(
return True

for extra, count in counts.items():
# Make sure the number of entries matches. If allow_3_only, then we
# just need to make sure we have at least the number of entries for
# supported Python 3 versions.
# Make sure the number of entries matches.
if count != len(global_reqs[name]):
if allow_3_only and count >= len(
_get_python3_reqs(global_reqs[name])
):
print(
"WARNING (probably OK for Ussuri and later): "
"Package '{}{}' is only tracking python 3 "
"requirements".format(
name, (f'[{extra}]') if extra else ''
)
)
continue

print(
"ERROR: Package '{}{}' requirement does not match "
"number of lines ({}) in "
Expand All @@ -324,7 +307,6 @@ def validate(
denylist,
global_reqs,
backports,
allow_3_only=False,
):
failed = False
# iterate through the changing entries and see if they match the global
Expand All @@ -339,7 +321,6 @@ def validate(
denylist,
global_reqs,
backports,
allow_3_only,
)
or failed
)
Expand Down
29 changes: 29 additions & 0 deletions openstack_requirements/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,35 @@ def _read_setup_cfg_extras(root: str) -> dict[str, list[str]] | None:
return result


def verify_pyproject_toml(root: str) -> bool:
data = _read_pyproject_toml(root)

if data is None:
print('Missing pyproject.toml file', file=sys.stderr)
return False

if 'build-system' not in data:
print("pyproject.toml is missing 'build-system' table", file=sys.stderr)
return False

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}",
file=sys.stderr,
)
return False

if 'project' not in data:
print(
"pyproject.toml is missing 'project' table. This is not currently "
"an error but may be in the future",
file=sys.stderr,
)

return True


class Project(TypedDict):
# The root directory path
root: str
Expand Down
108 changes: 0 additions & 108 deletions openstack_requirements/tests/test_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ def test_match_with_local_markers(self):
req,
self.global_reqs['name'],
self.backports,
allow_3_only=True,
)
)

Expand All @@ -133,7 +132,6 @@ def test_match_without_python3_markers(self):
req,
self.global_reqs['withmarker'],
self.backports,
allow_3_only=True,
)
)

Expand Down Expand Up @@ -451,110 +449,6 @@ def test_new_item_mismatches_global_list_with_extra(self):
)
)

def test_new_item_matches_py3_allowed_no_version(self):
# If the global list has multiple entries for an item but the branch
# allows python 3 only, then only the py3 entries need to match.
# Requirements without a python_version marker should always be used.
r_content = textwrap.dedent("""
name>=1.5;python_version=='3.5'
other-name
""")
reqs = [r for r, line in requirement.parse(r_content)['name']]
global_reqs = check.get_global_reqs(
textwrap.dedent("""
name>=1.5;python_version=='3.5'
name>=1.2,!=1.4;python_version=='2.6'
other-name
""")
)
self.assertFalse(
check._validate_one(
'name',
reqs=reqs,
denylist=requirement.parse(''),
backports=self.backports,
global_reqs=global_reqs,
allow_3_only=True,
)
)

def test_new_item_matches_py3_allowed(self):
# If the global list has multiple entries for an item but the branch
# allows python 3 only, then only the py3 entries need to match.
# Requirements without a python_version marker should always be used.
r_content = textwrap.dedent("""
name>=1.5
other-name
""")
reqs = [r for r, line in requirement.parse(r_content)['name']]
global_reqs = check.get_global_reqs(
textwrap.dedent("""
name>=1.5;python_version>='3.5'
name>=1.2,!=1.4;python_version=='2.6'
other-name
""")
)
self.assertFalse(
check._validate_one(
'name',
reqs=reqs,
denylist=requirement.parse(''),
backports=self.backports,
global_reqs=global_reqs,
allow_3_only=True,
)
)

def test_new_item_matches_py3_allowed_with_py2(self):
# If the global list has multiple entries for an item but the branch
# allows python 3 only, then only the py3 entries need to match.
# It should continue to pass with py2 entries though.
r_content = textwrap.dedent("""
name>=1.5;python_version=='3.5'
name>=1.2,!=1.4;python_version=='2.6'
""")
reqs = [r for r, line in requirement.parse(r_content)['name']]
global_reqs = check.get_global_reqs(
textwrap.dedent("""
name>=1.5;python_version=='3.5'
name>=1.2,!=1.4;python_version=='2.6'
""")
)
self.assertFalse(
check._validate_one(
'name',
reqs=reqs,
denylist=requirement.parse(''),
backports=self.backports,
global_reqs=global_reqs,
allow_3_only=True,
)
)

def test_new_item_matches_py3_allowed_no_py2(self):
# If the global list has multiple entries for an item but the branch
# allows python 3 only, then only the py3 entries need to match.
r_content = textwrap.dedent("""
name>=1.5;python_version=='3.5'
""")
reqs = [r for r, line in requirement.parse(r_content)['name']]
global_reqs = check.get_global_reqs(
textwrap.dedent("""
name>=1.5;python_version=='3.5'
name>=1.2,!=1.4;python_version=='2.6'
""")
)
self.assertFalse(
check._validate_one(
'name',
reqs=reqs,
denylist=requirement.parse(''),
backports=self.backports,
global_reqs=global_reqs,
allow_3_only=True,
)
)


class TestBackportPythonMarkers(testtools.TestCase):
def setUp(self):
Expand All @@ -581,7 +475,6 @@ def test_notmatching_no_backport(self):
self.req,
self.global_reqs["name"],
list(backports.keys()),
allow_3_only=True,
)
)

Expand All @@ -595,6 +488,5 @@ def test_notmatching_with_backport(self):
self.req,
self.global_reqs["name"],
list(backports.keys()),
allow_3_only=True,
)
)
68 changes: 68 additions & 0 deletions openstack_requirements/tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.

from io import StringIO
import os
import textwrap

Expand Down Expand Up @@ -56,6 +57,73 @@ def test_empty(self):
)


class TestVerifyPyprojectToml(testtools.TestCase):
def setUp(self):
super().setUp()
self.stderr = StringIO()
self.useFixture(fixtures.MonkeyPatch('sys.stderr', self.stderr))

def test_valid(self):
root = self.useFixture(common.pep_518_fixture).root
self.assertTrue(project.verify_pyproject_toml(root))
self.assertEqual('', self.stderr.getvalue())

def test_missing_file(self):
root = self.useFixture(fixtures.TempDir()).path
self.assertFalse(project.verify_pyproject_toml(root))
self.assertEqual(
'Missing pyproject.toml file\n', self.stderr.getvalue()
)

def test_missing_build_system(self):
root = self.useFixture(fixtures.TempDir()).path
with open(os.path.join(root, 'pyproject.toml'), 'w') as fh:
fh.write(
textwrap.dedent("""
[project]
name = "foo"
""")
)
self.assertFalse(project.verify_pyproject_toml(root))
self.assertIn(
"pyproject.toml is missing 'build-system' table",
self.stderr.getvalue(),
)

def test_invalid_build_backend(self):
root = self.useFixture(fixtures.TempDir()).path
with open(os.path.join(root, 'pyproject.toml'), 'w') as fh:
fh.write(
textwrap.dedent("""
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
""")
)
self.assertFalse(project.verify_pyproject_toml(root))
self.assertIn(
"pyproject.toml has invalid 'build-system.build-backend'. "
"Expected 'pbr.build'; got 'setuptools.build_meta'",
self.stderr.getvalue(),
)

def test_missing_project_table(self):
root = self.useFixture(fixtures.TempDir()).path
with open(os.path.join(root, 'pyproject.toml'), 'w') as fh:
fh.write(
textwrap.dedent("""
[build-system]
requires = ["pbr>=6.0.0", "setuptools>=64.0.0"]
build-backend = "pbr.build"
""")
)
self.assertTrue(project.verify_pyproject_toml(root))
self.assertIn(
"pyproject.toml is missing 'project' table",
self.stderr.getvalue(),
)


class TestProjectExtras(testtools.TestCase):
def test_pyproject_toml(self):
root = self.useFixture(fixtures.TempDir()).path
Expand Down
Loading