Bug 2351392 - fawltydeps fails to build with Python 3.14: AttributeError: module 'ast' has no attribute 'Str'
Summary: fawltydeps fails to build with Python 3.14: AttributeError: module 'ast' has ...
Keywords:
Status: CLOSED RAWHIDE
Alias: None
Product: Fedora
Classification: Fedora
Component: fawltydeps
Version: rawhide
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: ---
Assignee: Sandro
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks: PYTHON3.14
TreeView+ depends on / blocked
 
Reported: 2025-03-11 15:26 UTC by Karolina Surma
Modified: 2025-03-18 12:42 UTC (History)
3 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed: 2025-03-12 09:00:05 UTC
Type: Bug
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Fedora Package Sources fawltydeps pull-request 7 0 None None None 2025-03-11 23:50:08 UTC

Description Karolina Surma 2025-03-11 15:26:36 UTC
fawltydeps fails to build with Python 3.14.0a5.

Example failure:
_ test_find_and_parse_sources__project_with_pyproject_setup_and_requirements__returns_list _

fake_project = <function fake_project.<locals>.create_one_fake_project at 0x7fa0a75112d0>

    def test_find_and_parse_sources__project_with_pyproject_setup_and_requirements__returns_list(
        fake_project,
    ):
        tmp_path = fake_project(
            files_with_declared_deps={
                "requirements.txt": ["pandas", "click"],
                "subdir/requirements.txt": ["pandas", "tensorflow>=2"],
                "setup.py": (
                    ["pandas", "click>=1.2"],  # install_requires
                    {"annoy": ["annoy==1.15.2"], "chinese": ["jieba"]},  # extras_require
                ),
                "pyproject.toml": (
                    ["pandas", "pydantic>1.10.4"],  # dependencies
                    {"dev": ["pylint >= 2.15.8"]},  # optional-dependencies
                ),
            },
        )
        expect = [
            # from requirements.txt:
            "pandas",
            "click",
            # from subdir/requirements.txt:
            "pandas",
            "tensorflow",
            # from setup.py:
            "pandas",
            "click",
            "annoy",
            "jieba",
            # from pyproject.toml:
            "pandas",
            "pydantic",
            "pylint",
        ]
        settings = Settings(code=set(), deps={tmp_path})
        deps_sources = list(find_sources(settings, {DepsSource}))
        actual = collect_dep_names(parse_sources(deps_sources))
>       assert_unordered_equivalence(actual, expect)

tests/test_extract_deps_success.py:538: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/utils.py:49: in assert_unordered_equivalence
    actual_s = sorted(actual)
tests/utils.py:57: in <genexpr>
    return (dep.name for dep in deps)
fawltydeps/extract_deps/__init__.py:88: in parse_sources
    yield from parse_source(source)
fawltydeps/extract_deps/__init__.py:79: in parse_source
    yield from parser.execute(src.path)
fawltydeps/extract_deps/setup_py_parser.py:95: in parse_setup_py
    yield from _extract_deps_from_setup_call(node.value)  # type: ignore[attr-defined]
fawltydeps/extract_deps/setup_py_parser.py:59: in _extract_deps_from_setup_call
    value = tracked_vars.resolve(keyword.value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <fawltydeps.limited_eval.VariableTracker object at 0x7fa0a74e9730>
node = List(elts=[Constant(value='pandas', kind=None), Constant(value='click>=1.2', kind=None)], ctx=Load())

    def resolve(self, node: ast.AST) -> TrackedValue:
        """Convert a literal or a variable reference to the ultimate value.
    
        Raise CannotResolve if we're unable to arrive at the ultimate value,
        for example in these cases:
            - A kind of literal that we don't (yet) support.
            - A variable reference that we have been unable to resolve.
            - Anything that is not a literal or a variable reference, e.g. the
              result of a function call.
        """
        logger.debug(f"Resolving {self._dump(node)}")
        # Python v3.8 changed from ast.Str to ast.Constant
>       if isinstance(node, (ast.Constant, ast.Str)):
E       AttributeError: module 'ast' has no attribute 'Str'

=========================== short test summary info ============================
FAILED tests/test_cmdline.py::test_list_deps__pick_multiple_listed_files__prints_all_dependencies
FAILED tests/test_cmdline_options.py::test_options_interactions__correct_options__does_not_abort
FAILED tests/test_deps_parser_determination.py::test_explicit_parse_strategy__mismatch_yields_appropriate_logging[setup.py__pyproject.toml]
FAILED tests/test_deps_parser_determination.py::test_explicit_parse_strategy__mismatch_yields_appropriate_logging[setup.py__pixi.toml]
FAILED tests/test_deps_parser_determination.py::test_explicit_parse_strategy__mismatch_yields_appropriate_logging[setup.py__setup.py]
FAILED tests/test_extract_deps_errors.py::test_parse_setup_py__cannot_parse__logs_warning[lambda_call_in_install_requires]
FAILED tests/test_extract_deps_errors.py::test_parse_setup_py__cannot_parse__logs_warning[lambda_call_in_extras_require]
FAILED tests/test_extract_deps_errors.py::test_parse_setup_py__cannot_parse__logs_warning[lambda_call_inside_extras_require_dict]
FAILED tests/test_extract_deps_errors.py::test_parse_setup_py__cannot_parse__logs_warning[reference_to_unset_variable]
FAILED tests/test_extract_deps_errors.py::test_parse_setup_py__cannot_parse__logs_warning[unresolvable_self_reference]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[simple_requirements_in_setup_py__succeeds]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[requirements_with_versions__yields_names]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[handles_nested_functions__yields_names]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[two_setup_calls__uses_only_top_level]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[extras_present__yields_names]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[extras_and_regular_dependencies__yields_all_names]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[direct_list_variable_reference__succeeds]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[direct_dict_variable_reference__succeeds]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[variable_reference_inside_list__succeeds]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[variable_reference_inside_dict__succeeds]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[nested_variable_reference__succeeds]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[legacy_encoding__succeeds]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py[extras_with_varying_types]
FAILED tests/test_extract_deps_success.py::test_parse_setup_py__multiple_entries_in_extras_require__returns_list
FAILED tests/test_extract_deps_success.py::test_find_and_parse_sources__project_with_requirements_and_setup__returns_list
FAILED tests/test_extract_deps_success.py::test_find_and_parse_sources__project_with_pyproject_setup_and_requirements__returns_list
========= 26 failed, 1318 passed, 32 skipped, 61 deselected in 37.79s ==========

According to: https://docs.python.org/dev/whatsnew/3.14.html#id2

Remove the following classes. They were all deprecated since Python 3.8, and have emitted deprecation warnings since Python 3.12:
ast.Bytes
ast.Ellipsis
ast.NameConstant
ast.Num
ast.Str

Use ast.Constant instead. As a consequence of these removals, user-defined visit_Num, visit_Str, visit_Bytes, visit_NameConstant and visit_Ellipsis methods on custom ast.NodeVisitor subclasses will no longer be called when the NodeVisitor subclass is visiting an AST. Define a visit_Constant method instead.

Also, remove the following deprecated properties on ast.Constant, which were present for compatibility with the now-removed AST classes:
ast.Constant.n
ast.Constant.s

Use ast.Constant.value instead.
(Contributed by Alex Waygood in gh-119562.)


https://docs.python.org/3.14/whatsnew/3.14.html

For the build logs, see:
https://copr-be.cloud.fedoraproject.org/results/@python/python3.14/fedora-rawhide-x86_64/08746591-fawltydeps/

For all our attempts to build fawltydeps with Python 3.14, see:
https://copr.fedorainfracloud.org/coprs/g/python/python3.14/package/fawltydeps/

Testing and mass rebuild of packages is happening in copr.
You can follow these instructions to test locally in mock if your package builds with Python 3.14:
https://copr.fedorainfracloud.org/coprs/g/python/python3.14/

Let us know here if you have any questions.

Python 3.14 is planned to be included in Fedora 43.
To make that update smoother, we're building Fedora packages with all pre-releases of Python 3.14.
A build failure prevents us from testing all dependent packages (transitive [Build]Requires),
so if this package is required a lot, it's important for us to get it fixed soon.

We'd appreciate help from the people who know this package best,
but if you don't want to work on this now, let us know so we can try to work around it on our side.

Comment 1 Sandro 2025-03-11 23:50:09 UTC
Upstream already announced that the next release will no longer support Python 3.8 and is actively removing compatibility code:

https://github.com/tweag/FawltyDeps/commit/fa700308e61601bb29471611a1067b11812ce35b

I applied this commit as a patch. Do you need a build for rawhide? I saw Copr picked up the changes pushed out already.

Comment 2 Karolina Surma 2025-03-12 08:36:27 UTC
Thank you, I don't need a build in Rawhide, Copr is enough for me now (will be bumped during the mass rebuild in ~3 months).


Note You need to log in before you can comment on or make changes to this bug.