Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-flit-core for openSUSE:Factory checked in at 2025-05-07 19:14:34 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-flit-core (Old) and /work/SRC/openSUSE:Factory/.python-flit-core.new.30101 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-flit-core" Wed May 7 19:14:34 2025 rev:21 rq:1274041 version:3.12.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-flit-core/python-flit-core.changes 2025-03-17 22:20:31.493827000 +0100 +++ /work/SRC/openSUSE:Factory/.python-flit-core.new.30101/python-flit-core.changes 2025-05-07 19:14:36.110539634 +0200 @@ -1,0 +2,10 @@ +Fri May 2 14:17:24 UTC 2025 - Nico Krapp <nico.kr...@suse.com> + +- Update to 3.12.0 + * Support for license expressions using the AND and OR operators. + * Recognise __version__: str = "0.1" annotated assignments when + finding the version number. + * Clear error message when referring to a license file in a parent + directory, which is not supported. + +------------------------------------------------------------------- Old: ---- flit_core-3.11.0.tar.gz New: ---- flit_core-3.12.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-flit-core.spec ++++++ --- /var/tmp/diff_new_pack.yvORZP/_old 2025-05-07 19:14:36.694563996 +0200 +++ /var/tmp/diff_new_pack.yvORZP/_new 2025-05-07 19:14:36.698564163 +0200 @@ -52,7 +52,7 @@ %endif %{?sle15_python_module_pythons} Name: %{pprefix}-flit-core%{?psuffix} -Version: 3.11.0 +Version: 3.12.0 Release: 0 Summary: Distribution-building parts of Flit License: BSD-3-Clause AND MIT ++++++ flit_core-3.11.0.tar.gz -> flit_core-3.12.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/PKG-INFO new/flit_core-3.12.0/PKG-INFO --- old/flit_core-3.11.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/flit_core-3.12.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: flit_core -Version: 3.11.0 +Version: 3.12.0 Summary: Distribution-building parts of Flit. See flit package for more information Author-email: Thomas Kluyver & contributors <tho...@kluyver.me.uk> Requires-Python: >=3.6 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/flit_core/__init__.py new/flit_core-3.12.0/flit_core/__init__.py --- old/flit_core-3.11.0/flit_core/__init__.py 2025-02-19 09:21:57.541935200 +0100 +++ new/flit_core-3.12.0/flit_core/__init__.py 2025-03-25 09:03:05.503557200 +0100 @@ -4,4 +4,4 @@ All the convenient development features live in the main 'flit' package. """ -__version__ = '3.11.0' +__version__ = '3.12.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/flit_core/common.py new/flit_core-3.12.0/flit_core/common.py --- old/flit_core-3.11.0/flit_core/common.py 2025-02-19 09:21:57.542935100 +0100 +++ new/flit_core-3.12.0/flit_core/common.py 2025-03-25 09:03:05.503557200 +0100 @@ -148,22 +148,7 @@ with target_path.open('rb') as f: node = ast.parse(f.read()) for child in node.body: - if sys.version_info >= (3, 8): - target_type = ast.Constant - else: - target_type = ast.Str - # Only use the version from the given module if it's a simple - # string assignment to __version__ - is_version_str = ( - isinstance(child, ast.Assign) - and any( - isinstance(target, ast.Name) - and target.id == "__version__" - for target in child.targets - ) - and isinstance(child.value, target_type) - ) - if is_version_str: + if is_version_str_assignment(child): if sys.version_info >= (3, 8): version = child.value.value else: @@ -172,6 +157,20 @@ return ast.get_docstring(node), version +def is_version_str_assignment(node): + """Check if *node* is a simple string assignment to __version__""" + if not isinstance(node, (ast.Assign, ast.AnnAssign)): + return False + constant_type = ast.Constant if sys.version_info >= (3, 8) else ast.Str + if not isinstance(node.value, constant_type): + return False + targets = (node.target,) if isinstance(node, ast.AnnAssign) else node.targets + for target in targets: + if isinstance(target, ast.Name) and target.id == "__version__": + return True + return False + + # To ensure we're actually loading the specified file, give it a unique name to # avoid any cached import. In normal use we'll only load one module per process, # so it should only matter for the tests, but we'll do it anyway. @@ -379,7 +378,7 @@ return [item.strip() for item in list_str.split(',')] else: return None - + def _normalise_requires_dist(self, req): extras = self._extract_extras(req) if extras: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/flit_core/config.py new/flit_core-3.12.0/flit_core/config.py --- old/flit_core-3.11.0/flit_core/config.py 2025-02-19 09:21:57.542935100 +0100 +++ new/flit_core-3.12.0/flit_core/config.py 2025-03-25 09:03:05.504557400 +0100 @@ -4,7 +4,6 @@ import logging import os import os.path as osp -from os.path import isabs from pathlib import Path import re @@ -18,6 +17,7 @@ except ImportError: import tomli as tomllib +from ._spdx_data import licenses from .common import normalise_core_metadata_name from .versionno import normalise_version @@ -92,7 +92,7 @@ def prep_toml_config(d, path): """Validate config loaded from pyproject.toml and prepare common metadata - + Returns a LoadedConfig object. """ dtool = d.get('tool', {}).get('flit', {}) @@ -310,11 +310,11 @@ def _prep_metadata(md_sect, path): """Process & verify the metadata from a config file - + - Pull out the module name we're packaging. - Read description-file and check that it's valid rst - Convert dashes in key names to underscores - (e.g. home-page in config -> home_page in metadata) + (e.g. home-page in config -> home_page in metadata) """ if not set(md_sect).issuperset(metadata_required_fields): missing = metadata_required_fields - set(md_sect) @@ -473,7 +473,7 @@ ) try: files = [ - str(file.relative_to(project_dir)).replace(osp.sep, "/") + file.relative_to(project_dir).as_posix() for file in project_dir.glob(pattern) if file.is_file() ] @@ -587,7 +587,8 @@ if 'license' in proj: _check_types(proj, 'license', (str, dict)) if isinstance(proj['license'], str): - md_dict['license_expression'] = normalize_license_expr(proj['license']) + licence_expr = proj['license'] + md_dict['license_expression'] = normalise_compound_license_expr(licence_expr) else: license_tbl = proj['license'] unrec_keys = set(license_tbl.keys()) - {'text', 'file'} @@ -603,14 +604,20 @@ raise ConfigError( "[project.license] should specify file or text, not both" ) - license_f = license_tbl['file'] + license_f = osp.normpath(license_tbl['file']) if isabs_ish(license_f): raise ConfigError( - f"License file path ({license_f}) cannot be an absolute path" + f"License file path ({license_tbl['file']}) cannot be an absolute path" ) - if not (path.parent / license_f).is_file(): - raise ConfigError(f"License file {license_f} does not exist") - license_files.add(license_tbl['file']) + if license_f.startswith('..' + os.sep): + raise ConfigError( + f"License file path ({license_tbl['file']}) cannot contain '..'" + ) + license_p = path.parent / license_f + if not license_p.is_file(): + raise ConfigError(f"License file {license_tbl['file']} does not exist") + license_f = license_p.relative_to(path.parent).as_posix() + license_files.add(license_f) elif 'text' in license_tbl: pass else: @@ -816,34 +823,97 @@ return os.path.isabs(path) or path.startswith(('/', '\\')) -def normalize_license_expr(s: str): - """Validate & normalise an SPDX license expression +def normalise_compound_license_expr(s: str) -> str: + """Validate and normalise a compund SPDX license expression. + + Per the specification, licence expression operators (AND, OR and WITH) + are matched case-sensitively. The WITH operator is not currently supported. - For now this only handles simple expressions (referring to 1 license) + Spec: https://spdx.github.io/spdx-spec/v2.2.2/SPDX-license-expressions/ + """ + invalid_msg = "'{s}' is not a valid SPDX license expression: {reason}" + if not s or s.isspace(): + raise ConfigError(f"The SPDX license expression must not be empty") + + stack = 0 + parts = [] + try: + for part in filter(None, re.split(r' +|([()])', s)): + if part.upper() == 'WITH': + # provide a sensible error message for the WITH operator + raise ConfigError(f"The SPDX 'WITH' operator is not yet supported!") + elif part in {'AND', 'OR'}: + if not parts or parts[-1] in {' AND ', ' OR ', ' WITH ', '('}: + reason = f"a license ID is missing before '{part}'" + raise ConfigError(invalid_msg.format(s=s, reason=reason)) + parts.append(f' {part} ') + elif part.lower() in {'and', 'or', 'with'}: + # provide a sensible error message for non-uppercase operators + reason = f"operators must be uppercase, not '{part}'" + raise ConfigError(invalid_msg.format(s=s, reason=reason)) + elif part == '(': + if parts and parts[-1] not in {' AND ', ' OR ', '('}: + reason = f"'(' must follow either AND, OR, or '('" + raise ConfigError(invalid_msg.format(s=s, reason=reason)) + stack += 1 + parts.append(part) + elif part == ')': + if not parts or parts[-1] in {' AND ', ' OR ', ' WITH ', '('}: + reason = f"a license ID is missing before '{part}'" + raise ConfigError(invalid_msg.format(s=s, reason=reason)) + stack -= 1 + if stack < 0: + reason = 'unbalanced brackets' + raise ConfigError(invalid_msg.format(s=s, reason=reason)) + parts.append(part) + else: + if parts and parts[-1] not in {' AND ', ' OR ', '('}: + reason = f"a license ID must follow either AND, OR, or '('" + raise ConfigError(invalid_msg.format(s=s, reason=reason)) + simple_expr = normalise_simple_license_expr(part) + parts.append(simple_expr) + + if stack != 0: + reason = 'unbalanced brackets' + raise ConfigError(invalid_msg.format(s=s, reason=reason)) + if parts[-1] in {' AND ', ' OR ', ' WITH '}: + last_part = parts[-1].strip() + reason = f"a license ID or expression should follow '{last_part}'" + raise ConfigError(invalid_msg.format(s=s, reason=reason)) + except ConfigError: + if os.environ.get('FLIT_ALLOW_INVALID'): + log.warning(f"Invalid license ID {s!r} allowed by FLIT_ALLOW_INVALID") + return s + raise + + return ''.join(parts) + + +def normalise_simple_license_expr(s: str) -> str: + """Normalise a simple SPDX license expression. + + https://spdx.github.io/spdx-spec/v2.2.2/SPDX-license-expressions/#d3-simple-license-expressions """ - from ._spdx_data import licenses ls = s.lower() if ls.startswith('licenseref-'): - ref = s.partition('-')[2] - if re.match(r'([a-zA-Z0-9\-.])+$', ref): + ref = s[11:] + if re.fullmatch(r'[a-zA-Z0-9\-.]+', ref): # Normalise case of LicenseRef, leave the rest alone - return "LicenseRef-" + ref + return f"LicenseRef-{ref}" raise ConfigError( "LicenseRef- license expression can only contain ASCII letters " "& digits, - and ." ) - or_later = s.endswith('+') + or_later = ls.endswith('+') if or_later: ls = ls[:-1] try: - info = licenses[ls] + normalised_id = licenses[ls]['id'] except KeyError: - if os.environ.get('FLIT_ALLOW_INVALID'): - log.warning("Invalid license ID {!r} allowed by FLIT_ALLOW_INVALID" - .format(s)) - return s raise ConfigError(f"{s!r} is not a recognised SPDX license ID") - return info['id'] + ('+' if or_later else '') + if or_later: + return f'{normalised_id}+' + return normalised_id diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/flit_core/vendor/tomli-1.2.3.dist-info/METADATA new/flit_core-3.12.0/flit_core/vendor/tomli-1.2.3.dist-info/METADATA --- old/flit_core-3.11.0/flit_core/vendor/tomli-1.2.3.dist-info/METADATA 2025-02-19 09:21:57.542935100 +0100 +++ new/flit_core-3.12.0/flit_core/vendor/tomli-1.2.3.dist-info/METADATA 2025-03-25 09:03:05.504557400 +0100 @@ -205,4 +205,3 @@ The parsers are ordered from fastest to slowest, using the fastest parser as baseline. Tomli performed the best out of all pure Python TOML parsers, losing only to pytomlpp (wraps C++) and rtoml (wraps Rust). - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/flit_core/versionno.py new/flit_core-3.12.0/flit_core/versionno.py --- old/flit_core-3.11.0/flit_core/versionno.py 2025-02-19 09:21:57.542935100 +0100 +++ new/flit_core-3.12.0/flit_core/versionno.py 2025-03-25 09:03:05.504557400 +0100 @@ -124,4 +124,3 @@ log.warning("Version number normalised: {!r} -> {!r} (see PEP 440)" .format(orig_version, version)) return version - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/tests_core/samples/annotated_version/module1.py new/flit_core-3.12.0/tests_core/samples/annotated_version/module1.py --- old/flit_core-3.11.0/tests_core/samples/annotated_version/module1.py 1970-01-01 01:00:00.000000000 +0100 +++ new/flit_core-3.12.0/tests_core/samples/annotated_version/module1.py 2025-03-25 09:03:05.505557300 +0100 @@ -0,0 +1,4 @@ + +"""This module has a __version__ that has a type annotation""" + +__version__: str = '0.1' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/tests_core/samples/annotated_version/pyproject.toml new/flit_core-3.12.0/tests_core/samples/annotated_version/pyproject.toml --- old/flit_core-3.11.0/tests_core/samples/annotated_version/pyproject.toml 1970-01-01 01:00:00.000000000 +0100 +++ new/flit_core-3.12.0/tests_core/samples/annotated_version/pyproject.toml 2025-03-25 09:03:05.505557300 +0100 @@ -0,0 +1,12 @@ +[build-system] +requires = ["flit_core >=2,<4"] +build-backend = "flit_core.buildapi" + +[tool.flit.metadata] +module = "module1" +author = "Sir Robin" +author-email = "ro...@camelot.uk" +home-page = "http://github.com/sirrobin/module1" +requires = [ + "numpy >=1.16.0", +] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/tests_core/samples/imported_version/package1/__init__.py new/flit_core-3.12.0/tests_core/samples/imported_version/package1/__init__.py --- old/flit_core-3.11.0/tests_core/samples/imported_version/package1/__init__.py 2025-02-19 09:21:57.543935000 +0100 +++ new/flit_core-3.12.0/tests_core/samples/imported_version/package1/__init__.py 2025-03-25 09:03:05.505557300 +0100 @@ -2,4 +2,4 @@ from ._version import __version__ -import a_package_that_doesnt_exist \ No newline at end of file +import a_package_that_doesnt_exist diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/tests_core/samples/invalid_version1.py new/flit_core-3.12.0/tests_core/samples/invalid_version1.py --- old/flit_core-3.11.0/tests_core/samples/invalid_version1.py 2025-02-19 09:21:57.543935000 +0100 +++ new/flit_core-3.12.0/tests_core/samples/invalid_version1.py 2025-03-25 09:03:05.505557300 +0100 @@ -1,3 +1,3 @@ """Sample module with invalid __version__ string""" -__version__ = "not starting with a number" \ No newline at end of file +__version__ = "not starting with a number" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/tests_core/samples/ns1-pkg/ns1/pkg/__init__.py new/flit_core-3.12.0/tests_core/samples/ns1-pkg/ns1/pkg/__init__.py --- old/flit_core-3.11.0/tests_core/samples/ns1-pkg/ns1/pkg/__init__.py 2025-02-19 09:21:57.544935200 +0100 +++ new/flit_core-3.12.0/tests_core/samples/ns1-pkg/ns1/pkg/__init__.py 2025-03-25 09:03:05.506557200 +0100 @@ -5,4 +5,3 @@ """ __version__ = '0.1' - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/tests_core/test_common.py new/flit_core-3.12.0/tests_core/test_common.py --- old/flit_core-3.11.0/tests_core/test_common.py 2025-02-19 09:21:57.545935200 +0100 +++ new/flit_core-3.12.0/tests_core/test_common.py 2025-03-25 09:03:05.508557300 +0100 @@ -75,6 +75,11 @@ 'version': '0.1'} ) + info = get_info_from_module(Module('module1', samples_dir / 'annotated_version')) + self.assertEqual(info, {'summary': 'This module has a __version__ that has a type annotation', + 'version': '0.1'} + ) + info = get_info_from_module(Module('module1', samples_dir / 'constructed_version')) self.assertEqual(info, {'summary': 'This module has a __version__ that requires runtime interpretation', 'version': '1.2.3'} @@ -210,8 +215,12 @@ ('value', 'expected_license', 'expected_license_expression'), [ ({'license': 'MIT'}, 'MIT', None), + ({'license': 'MIT OR Apache-2.0'}, 'MIT OR Apache-2.0', None), + ({'license': 'MIT AND Apache-2.0'}, 'MIT AND Apache-2.0', None), ({'license_expression': 'MIT'}, None, 'MIT'), ({'license_expression': 'Apache-2.0'}, None, 'Apache-2.0'), + ({'license_expression': 'MIT OR Apache-2.0'}, None, 'MIT OR Apache-2.0'), + ({'license_expression': 'MIT AND Apache-2.0'}, None, 'MIT AND Apache-2.0'), ], ) def test_metadata_license(value, expected_license, expected_license_expression): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/tests_core/test_config.py new/flit_core-3.12.0/tests_core/test_config.py --- old/flit_core-3.11.0/tests_core/test_config.py 2025-02-19 09:21:57.545935200 +0100 +++ new/flit_core-3.12.0/tests_core/test_config.py 2025-03-25 09:03:05.508557300 +0100 @@ -1,4 +1,5 @@ import logging +import re import sys from pathlib import Path import pytest @@ -139,6 +140,14 @@ ({'version': 1}, r'\bstr\b'), ({'license': {'fromage': 2}}, '[Uu]nrecognised'), ({'license': {'file': 'LICENSE', 'text': 'xyz'}}, 'both'), + ( + {'license': {'file': '/LICENSE'}}, + re.escape("License file path (/LICENSE) cannot be an absolute path"), + ), + ( + {'license': {'file': '../LICENSE'}}, + re.escape("License file path (../LICENSE) cannot contain '..'"), + ), ({'license': {}}, 'required'), ({'license': 1}, "license field should be <class 'str'> or <class 'dict'>, not <class 'int'>"), # ({'license': "MIT License"}, "Invalid license expression: 'MIT License'"), # TODO @@ -213,8 +222,17 @@ ("mit", "MIT"), ("apache-2.0", "Apache-2.0"), ("APACHE-2.0+", "Apache-2.0+"), - # TODO: compound expressions - #("mit and (apache-2.0 or bsd-2-clause)", "MIT AND (Apache-2.0 OR BSD-2-Clause)"), + ("mit AND (apache-2.0 OR bsd-2-clause)", "MIT AND (Apache-2.0 OR BSD-2-Clause)"), + ("(mit)", "(MIT)"), + ("MIT OR Apache-2.0", "MIT OR Apache-2.0"), + ("MIT AND Apache-2.0", "MIT AND Apache-2.0"), + ("MIT AND Apache-2.0+ OR 0BSD", "MIT AND Apache-2.0+ OR 0BSD"), + ("MIT AND (Apache-2.0+ OR (0BSD))", "MIT AND (Apache-2.0+ OR (0BSD))"), + ("MIT OR(mit)", "MIT OR (MIT)"), + ("(mit)AND mit", "(MIT) AND MIT"), + ("MIT OR (MIT OR ( MIT )) AND ((MIT) AND MIT) OR MIT", "MIT OR (MIT OR (MIT)) AND ((MIT) AND MIT) OR MIT"), + ("LICENSEREF-Public-Domain OR cc0-1.0 OR unlicense", "LicenseRef-Public-Domain OR CC0-1.0 OR Unlicense"), + ("mit AND ( apache-2.0+ OR mpl-2.0+ )", "MIT AND (Apache-2.0+ OR MPL-2.0+)"), # LicenseRef expressions: only the LicenseRef is normalised ("LiceNseref-Public-DoMain", "LicenseRef-Public-DoMain"), ]) @@ -226,19 +244,143 @@ assert 'license' not in info.metadata assert info.metadata['license_expression'] == license_expression -def test_license_expr_error(): +@pytest.mark.parametrize('invalid_expr', [ + "LicenseRef-foo_bar", + "LicenseRef-foo~bar", + "LicenseRef-foo:bar", + "LicenseRef-foo[bar]", + "LicenseRef-foo-bar+", +]) +def test_license_expr_error_licenseref(invalid_expr: str): proj = { 'name': 'module1', 'version': '1.0', 'description': 'x', - 'license': 'LicenseRef-foo_bar', # Underscore not allowed + 'license': invalid_expr, } with pytest.raises(config.ConfigError, match="can only contain"): config.read_pep621_metadata(proj, samples_dir / 'pep621' / 'pyproject.toml') - proj['license'] = "BSD-33-Clause" # Not a real license + +@pytest.mark.parametrize('invalid_expr', [ + # Not a real licence + "BSD-33-Clause", + "MIT OR BSD-33-Clause", + "MIT OR (MIT AND BSD-33-Clause)", +]) +def test_license_expr_error_not_recognised(invalid_expr: str): + proj = { + 'name': 'module1', 'version': '1.0', 'description': 'x', + 'license': invalid_expr, + } with pytest.raises(config.ConfigError, match="recognised"): config.read_pep621_metadata(proj, samples_dir / 'pep621' / 'pyproject.toml') +@pytest.mark.parametrize('invalid_expr', [ + # No operator + "MIT MIT", + "MIT OR (MIT MIT)", + # Only operator + "AND", + "OR", + "AND AND AND", + "OR OR OR", + "OR AND OR", + "AND OR OR AND OR OR AND", + # Too many operators + "MIT AND AND MIT", + "MIT OR OR OR MIT", + "MIT AND OR MIT", + # Mixed case operator + "MIT aND MIT", + "MIT oR MIT", + "MIT AND MIT oR MIT", + # Missing operand + "MIT AND", + "AND MIT", + "MIT OR", + "OR MIT", + "MIT (AND MIT)", + "(MIT OR) MIT", + # Unbalanced brackets + ")(", + "(", + ")", + "MIT OR ()", + ") AND MIT", + "MIT OR (", + "MIT OR (MIT))", + # Only brackets + "()", + "()()", + "()(())", + "( )", + " ( )", + "( ) ", + " ( ) ", +]) +def test_license_expr_error(invalid_expr: str): + proj = { + 'name': 'module1', 'version': '1.0', 'description': 'x', + 'license': invalid_expr, + } + with pytest.raises(config.ConfigError, match="is not a valid"): + config.read_pep621_metadata(proj, samples_dir / 'pep621' / 'pyproject.toml') + + +@pytest.mark.parametrize('invalid_expr', [ + "", + " ", + "\t", + "\r", + "\n", + "\f", + " \t \n \r \f ", +]) +def test_license_expr_error_empty(invalid_expr: str): + proj = { + 'name': 'module1', 'version': '1.0', 'description': 'x', + 'license': invalid_expr, + } + with pytest.raises(config.ConfigError, match="must not be empty"): + config.read_pep621_metadata(proj, samples_dir / 'pep621' / 'pyproject.toml') + + +@pytest.mark.parametrize('invalid_expr', [ + "mit or mit", + "or", + "and", + "MIT and MIT", + "MIT AND MIT or MIT", + "MIT AND (MIT or MIT)", +]) +def test_license_expr_error_lowercase(invalid_expr: str): + proj = { + 'name': 'module1', 'version': '1.0', 'description': 'x', + 'license': invalid_expr, + } + with pytest.raises(config.ConfigError, match="must be uppercase"): + config.read_pep621_metadata(proj, samples_dir / 'pep621' / 'pyproject.toml') + + +@pytest.mark.parametrize('invalid_expr', [ + "WITH", + "with", + "WiTh", + "wiTH", + "MIT WITH MIT-Exception", + "(MIT WITH MIT-Exception)", + "MIT OR MIT WITH MIT-Exception", + "MIT WITH MIT-Exception OR (MIT AND MIT)", +]) +def test_license_expr_error_unsupported_with(invalid_expr: str): + proj = { + 'name': 'module1', 'version': '1.0', 'description': 'x', + 'license': invalid_expr, + } + with pytest.raises(config.ConfigError, match="not yet supported"): + config.read_pep621_metadata(proj, samples_dir / 'pep621' / 'pyproject.toml') + + def test_license_file_defaults_with_old_metadata(): metadata = {'module': 'mymod', 'author': ''} info = config._prep_metadata(metadata, samples_dir / 'pep621_license_files' / 'pyproject.toml') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flit_core-3.11.0/tests_core/test_wheel.py new/flit_core-3.12.0/tests_core/test_wheel.py --- old/flit_core-3.11.0/tests_core/test_wheel.py 2025-02-19 09:21:57.546935000 +0100 +++ new/flit_core-3.12.0/tests_core/test_wheel.py 2025-03-25 09:03:05.508557300 +0100 @@ -39,7 +39,7 @@ with ZipFile(wheels[0], 'r') as zf: assert 'module1a.py' in zf.namelist() - + def test_data_dir(tmp_path): info = make_wheel_in(samples_dir / 'with_data_dir' / 'pyproject.toml', tmp_path) assert_isfile(info.file)