PyPI / setuptools / 84.0.0

setuptools 84.0.0

Source
https://github.com/pypa/setuptools
Tag scanned
v84.0.0
Compared with
v83.0.0 (5 signals introduced since)
Files
410 scanned, 127 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 59 info
Scan time
21.8 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 22 of 22 warn and critical signals; 22 demoted to info.
Disclude version
4.0.0
Scanned (UTC)
2026-10-14 18:10

Signals

Ordered by severity, then signals introduced in this release. Hidden characters are shown as ⟨U+…⟩.

  1. info warn → info by review dynamic-execution setuptools/_distutils/core.py:250:17 new in this release

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

                    code = f.read().replace(r'\r\n', r'\n')
                    exec(code, g)  # noqa: S102 # executing the setup script is the point
            finally:
                sys.argv = save_argv
    likely benign LLM, confidence 0.90 exec of a project's setup script inside run_setup, by design.
    Reasoning

    This executes a setup.py script inside run_setup, which is the documented purpose of the function. The exec is intentional and the noqa comment acknowledges it.

  2. info warn → info by review dynamic-execution setuptools/build_meta.py:317:13 new in this release

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

            try:
                exec(code, locals())  # noqa: S102 # exec is intentional here
            except SystemExit as e:
                if e.code:
    likely benign LLM, confidence 0.90 exec of the setup script in the PEP 517 build backend, by design.
    Reasoning

    exec runs the project's setup script in the PEP 517 build backend, which is the intended behaviour. The code is marked noqa with an explicit comment that the exec is intentional.

  3. info critical → info by review dynamic-import setuptools/extension.py:24:9 new in this release

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

            # from (cython_impl) import build_ext
            __import__(cython_impl, fromlist=['build_ext']).build_ext  # noqa: B018 # evaluated to trigger validation/side effect
        except Exception:  # noqa: BLE001 # intentional broad fallback
            return False
    likely benign LLM, confidence 0.80 Dynamically imports the Cython build_ext module to test whether Cython support is available.
    Reasoning

    The __import__ call is wrapped in a broad try/except and deliberately evaluated to check whether the Cython build integration is usable. The comment explains the intent, and the target is a fixed internal module name rather than attacker-controlled input.

  4. info warn → info by review dynamic-execution setuptools/launch.py:32:5 new in this release

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

        code = compile(norm_script, script_name, 'exec')
        exec(code, namespace)  # noqa: S102 # exec is intentional here
    likely benign LLM, confidence 0.90 Executes the compiled setup script within a provided namespace as the launcher's core function.
    Reasoning

    The exec runs the compiled setup script in a namespace the launcher controls, which is the documented behavior of setuptools' launch helper that mimics running setup.py. The code is annotated as intentional and nothing in the snippet is obfuscated or remote.

  5. info warn → info by review dynamic-execution setuptools/tests/test_editable_install.py:448:9 new in this release

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

            loc = {}
            exec(finder, loc, loc)  # noqa: S102 # exec is intentional here
            loc["install"]()
    likely benign LLM, confidence 0.85 Test executes a generated editable-install finder and calls its install function.
    Reasoning

    This test execs a generated finder module in a local dict and then calls its install hook, which verifies editable-install behavior. The code is in a test file, and the finder content is generated by the test itself, so the risk is low.

  6. info critical → info by review dynamic-execution launcher.c:328:9

    `execv` called with a non-literal path (ast pass, confidence 0.90)

            /* Use exec, we don't need to wait for the GUI to finish */
            execv(ptr, (const char * const *)(newargs));
            return fail("Could not exec %s", ptr);   /* shouldn't get here! */
        }
    likely benign LLM, confidence 0.80 execv in a C launcher that starts a runtime-determined executable.
    Reasoning

    execv with a computed path is the standard pattern for a Windows GUI launcher that starts a configured interpreter or script. The snippet shows no obfuscation, encoded payload, or hidden trigger, though the path source is not visible.

  7. info critical → info by review dynamic-execution setuptools/_distutils/command/bdist_rpm.py:369:15

    `os.popen` called on a non-literal command — runtime-determined shell invocation (ast pass, confidence 0.85)

    
            out = os.popen(q_cmd)
            try:
                binary_rpms = []
    likely benign LLM, confidence 0.85 os.popen on a constructed rpm query command during RPM packaging.
    Reasoning

    os.popen runs a query command built from RPM tooling arguments inside distutils' bdist_rpm command. This is ordinary build-tool shell use with no encoding or concealment.

  8. info warn → info by review dynamic-execution setuptools/_distutils/command/bdist_rpm.py:436:23

    `subprocess.getoutput` called with a literal shell command (ast pass, confidence 0.70)

            # RHEL6 (and probably derivatives)
            vendor_hook = subprocess.getoutput('rpm --eval %{__os_install_post}')
            # Generate a potential replacement value for __os_install_post (whilst
            # normalizing the whitespace to simplify the test for whether the
    dismissed LLM, confidence 0.90 Fixed rpm --eval shell call to detect a vendor-specific hook.
    Reasoning

    The command is a fixed literal rpm --eval call, used to detect the vendor's install post hook. It is a known, harmless distutils idiom.

  9. info warn → info by review dynamic-execution setuptools/_distutils/compilers/C/base.py:1130:28

    `eval` called on a non-literal expression (ast pass, confidence 0.70)

            expected = '"static", "shared", "dylib", "xcode_stub"'
            if lib_type not in eval(expected):
                raise ValueError(f"'lib_type' must be {expected}")
            fmt = getattr(self, lib_type + "_lib_format")
    likely benign LLM, confidence 0.90 eval of a constant string to build a set of valid library types.
    Reasoning

    eval is applied to a hardcoded local string literal listing valid library types. The input is not attacker-controlled, so this is a trivial validation idiom.

  10. info critical → info by review dynamic-import setuptools/_distutils/dist.py:873:17

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

                try:
                    __import__(module_name)
                    module = sys.modules[module_name]
                except ImportError:
    likely benign LLM, confidence 0.85 Dynamic import of a command module by name in distutils.
    Reasoning

    __import__ on a module name is used to load distutils command modules dynamically. This is the standard plugin mechanism and the import is wrapped in a normal ImportError handler.

  11. identifier `Dεbug` mixes scripts: Latin + Greek (in string/comment) (raw pass, confidence 0.40)

            caplog.set_level(logging.DEBUG)
            log.debug('Dεbug\tMėssãge')
            log.fatal('Fαtal\tÈrrōr')
            assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
  12. identifier `Fαtal` contains homoglyph candidates: α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

            log.debug('Dεbug\tMėssãge')
            log.fatal('Fαtal\tÈrrōr')
            assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
  13. identifier `Fαtal` mixes scripts: Latin + Greek (in string/comment) (raw pass, confidence 0.40)

            log.debug('Dεbug\tMėssãge')
            log.fatal('Fαtal\tÈrrōr')
            assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
  14. identifier `Dεbug` mixes scripts: Latin + Greek (in string/comment) (raw pass, confidence 0.40)

            log.fatal('Fαtal\tÈrrōr')
            assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
  15. identifier `Fαtal` contains homoglyph candidates: α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

            log.fatal('Fαtal\tÈrrōr')
            assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
  16. identifier `Fαtal` mixes scripts: Latin + Greek (in string/comment) (raw pass, confidence 0.40)

            log.fatal('Fαtal\tÈrrōr')
            assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
  17. info warn → info by review dynamic-execution setuptools/_distutils/util.py:397:21

    `compile` called on a non-literal expression (ast pass, confidence 0.70)

                        log.info("byte-compiling %s to %s", file, cfile_base)
                        compile(file, cfile, dfile)
                    else:
                        log.debug("skipping byte-compilation of %s to %s", file, cfile_base)
    likely benign LLM, confidence 0.90 Byte-compilation of Python files during install.
    Reasoning

    compile is py_compile-style byte-compilation of installed Python files, which is normal install behaviour, and it is logged alongside the call.

  18. info warn → info by review decoder-decompress-payload setuptools/_vendor/backports/tarfile/__init__.py:479:20

    file imports decoder module(s) zlib, bz2, lzma, gzip and calls `.decompress()` — embedded payload decompressed at runtime (ast pass, confidence 0.75)

            """
            self.cmp = self.zlib.decompressobj(-self.zlib.MAX_WBITS)
            self.dbuf = b""
    dismissed LLM, confidence 0.90 Streaming decompression of tar archive data in the tarfile backport.
    Reasoning

    zlib decompressobj is used to stream-decompress tar archive data in the tarfile backport. Decompression of input archives is the library's core function, not a hidden payload.

  19. info warn → info by review dynamic-execution setuptools/_vendor/jaraco/functools/__init__.py:563:28

    `eval` called on a non-literal expression (ast pass, confidence 0.70)

                    try:
                        return eval(use)
                    except TypeError:
                        return replace
    likely benign LLM, confidence 0.70 eval of a configured expression with a fallback on TypeError in jaraco.functools.
    Reasoning

    eval is applied to a variable named use, which likely holds a configured expression or name. The snippet does not show where it comes from, so an untrusted source cannot be ruled out, but the context looks like a benign fallback helper.

  20. info warn → info by review dynamic-execution setuptools/_vendor/packaging/licenses/__init__.py:105:9

    `compile` called on a non-literal expression (ast pass, confidence 0.70)

        try:
            compile(python_expression, "", "eval")
        except SyntaxError:
            message = f"Invalid license expression: {raw_license_expression!r}"
    likely benign LLM, confidence 0.90 compile-only syntax validation of a license expression string.
    Reasoning

    compile with mode eval is used only as a syntax check on a license expression, and the SyntaxError is caught to report an invalid expression. Nothing is executed.

  21. info warn → info by review dynamic-execution setuptools/command/bdist_egg.py:396:12

    `marshal.load` deserializes Python bytecode from bytes — code-object sink (ast pass, confidence 0.75)

        f.read(skip)
        code = marshal.load(f)
        f.close()
        safe = True
    likely benign LLM, confidence 0.90 marshal.load of a .pyc to inspect module bytecode for zip safety.
    Reasoning

    marshal.load reads a code object from a .pyc file in order to scan a module for zip-safety, which is the purpose of this function. It is a standard inspection step, not a payload launch.

  22. info warn → info by review frame-introspection setuptools/command/install.py:114:18

    `inspect.getouterframes` — call-stack frame introspection (ast pass, confidence 0.70)

    
            frames = inspect.getouterframes(run_frame)
            for frame in frames[2:4]:
                (caller,) = frame[:1]
    likely benign LLM, confidence 0.85 Walks the call stack with inspect.getouterframes to inspect caller frames during install.
    Reasoning

    inspect.getouterframes is used to walk the call stack to find a caller frame, a known pattern for detecting how the install command was invoked. The snippet shows no network, decoding, or payload activity, and setuptools is a well-known build tool, so this reads as legitimate.

  23. line length 1035 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system must be object", value=data__buildsystem, name="" + (name_prefix or "data") + ".build-system", definition={'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': 
  24. line length 792 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups must be object", value=data__dependencygroups, name="" + (name_prefix or "data") + ".dependency-groups", definition={'type': 'object', 'description': 'Dependency groups following PEP 735', 'additionalProperties': False, 'p
  25. line length 891 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be object", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.
  26. line length 968 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be object", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using
  27. line length 934 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be object", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded',
  28. line length 1720 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".ext-modules must be array", value=data__extmodules, name="" + (name_prefix or "data") + ".ext-modules", definition={'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$id': '#/definitions/ext-module
  29. line length 791 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".data-files must be object", value=data__datafiles, name="" + (name_prefix or "data") + ".data-files", definition={'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns t
  30. line length 1584 characters (raw pass, confidence 0.50)

            raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'req
  31. line length 1643 characters (raw pass, confidence 0.50)

                raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class
  32. line length 1636 characters (raw pass, confidence 0.50)

                raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extens
  33. line length 1230 characters (raw pass, confidence 0.50)

            raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$desc
  34. line length 1110 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".find must be object", value=data__find, name="" + (name_prefix or "data") + ".find", definition={'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.']
  35. line length 1282 characters (raw pass, confidence 0.50)

                raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties':
  36. line length 1502 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be valid exactly by one definition" + (" (" + str(data__readme_one_of_count12) + " matches found)"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'$$description': ['`Full/detailed description of
  37. line length 935 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be valid exactly by one definition" + (" (" + str(data__license_one_of_count14) + " matches found)"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'description': '`Project license <https://pe
  38. line length 963 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".authors must be array", value=data__authors, name="" + (name_prefix or "data") + ".authors", definition={'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-062
  39. line length 920 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".maintainers must be array", value=data__maintainers, name="" + (name_prefix or "data") + ".maintainers", definition={'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python
  40. line length 837 characters (raw pass, confidence 0.50)

                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic[{data__dynamic_x}]".format(**locals()) + " must be one of ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scrip
  41. line length 968 characters (raw pass, confidence 0.50)

            raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capa
  42. line length 1020 characters (raw pass, confidence 0.50)

                raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped to
  43. line length 1011 characters (raw pass, confidence 0.50)

                    raise JsonSchemaValueException("" + (name_prefix or "data") + " must be named by propertyName definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped toge
  44. info warn → info by review dynamic-execution setuptools/depends.py:132:24

    `marshal.load` deserializes Python bytecode from bytes — code-object sink (ast pass, confidence 0.75)

                    f.read(8)  # skip magic & date
                    code = marshal.load(f)
                elif kind == PY_FROZEN:
                    code = _imp.get_frozen_object(module, paths)
    likely benign LLM, confidence 0.85 Loads a .pyc code object with marshal.load to inspect a module without importing it.
    Reasoning

    marshal.load here reads cached Python bytecode from a module file after skipping the magic and date header. This is how setuptools inspects module constants for version detection, a documented and expected use inside a packaging library, not an injected payload.

  45. info warn → info by review decoder-import-with-exec setuptools/depends.py:136:24

    file imports decoder module(s) marshal and calls `exec`/`eval`/`compile` — multi-stage payload shape (ast pass, confidence 0.80)

                elif kind == PY_SOURCE:
                    code = compile(f.read(), path, 'exec')
                else:
                    # Not something we can parse; we'll have to import it.  :(
    likely benign LLM, confidence 0.85 Compiles module source or loads bytecode to analyze a module's contents, branching on file kind.
    Reasoning

    The compile and marshal pairing is the usual module-inspection logic: source files are compiled and bytecode files are unmarshalled, depending on module kind, so the decoder-plus-exec shape is expected. There is no encoded blob or remote fetch in the snippet.

  46. info warn → info by review dynamic-execution setuptools/depends.py:136:24

    `compile` called on a non-literal expression (ast pass, confidence 0.70)

                elif kind == PY_SOURCE:
                    code = compile(f.read(), path, 'exec')
                else:
                    # Not something we can parse; we'll have to import it.  :(
    likely benign LLM, confidence 0.85 Compiles module source read from disk during module introspection.
    Reasoning

    compile is applied to the contents of a file the package is already analyzing, in a branch selected by module kind. The argument is data read from the project's own file, which is normal for a module-inspection helper.

  47. info warn → info by review dynamic-execution setuptools/launch.py:31:12

    `compile` called on a non-literal expression (ast pass, confidence 0.70)

        norm_script = script.replace('\\r\\n', '\\n')
        code = compile(norm_script, script_name, 'exec')
        exec(code, namespace)  # noqa: S102 # exec is intentional here
    likely benign LLM, confidence 0.90 Compiles a setup script's source so the launcher can execute it.
    Reasoning

    launch.py is setuptools' script runner: it reads a setup script, normalizes line endings, and compiles it so it can run under a controlled namespace. Compiling the setup script is the purpose of the module, and the code carries an explicit noqa comment stating the exec is intentional.

  48. identifier `Γαμα` contains homoglyph candidates: α (03B1)→a, α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

      {name = "John X. Ãørçeč", email = "john@utf8.org"},
      {name = "Γαμα קּ 東", email = "gama@utf8.org"},
    ]
    classifiers = [
  49. identifier `Γαμα` mixes scripts: Greek + Latin (in string/comment) (raw pass, confidence 0.40)

      {name = "John X. Ãørçeč", email = "john@utf8.org"},
      {name = "Γαμα קּ 東", email = "gama@utf8.org"},
    ]
    classifiers = [
  50. identifier `Γαμα` contains homoglyph candidates: α (03B1)→a, α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

                    'Brett Cannon <brett@python.org>, "John X. Ãørçeč" <john@utf8.org>, '
                    'Γαμα קּ 東 <gama@utf8.org>'
                ),
                id='non-international-emails',
  51. identifier `Γαμα` mixes scripts: Greek + Latin (in string/comment) (raw pass, confidence 0.40)

                    'Brett Cannon <brett@python.org>, "John X. Ãørçeč" <john@utf8.org>, '
                    'Γαμα קּ 東 <gama@utf8.org>'
                ),
                id='non-international-emails',
  52. identifier `Γαμα` contains homoglyph candidates: α (03B1)→a, α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

                version = 42
                author_email = "John X. Ãørçeč" <john@utf8.org>, Γαμα קּ 東 <gama@utf8.org>
                long_description = file: README.rst
                """
  53. identifier `Γαμα` mixes scripts: Greek + Latin (in string/comment) (raw pass, confidence 0.40)

                version = 42
                author_email = "John X. Ãørçeč" <john@utf8.org>, Γαμα קּ 東 <gama@utf8.org>
                long_description = file: README.rst
                """
  54. identifier `Γαμα` contains homoglyph candidates: α (03B1)→a, α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

    Version: 42
    Author-email: "John X. Ãørçeč" <john@utf8.org>, Γαμα קּ 東 <gama@utf8.org>
  55. identifier `Γαμα` mixes scripts: Greek + Latin (in string/comment) (raw pass, confidence 0.40)

    Version: 42
    Author-email: "John X. Ãørçeč" <john@utf8.org>, Γαμα קּ 東 <gama@utf8.org>
  56. identifier `Γαμα` contains homoglyph candidates: α (03B1)→a, α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

        assert 'Author-email: "John X. Ãørçeč"' in metadata
        assert "Γαμα קּ 東 " in metadata
        assert "UTF-8 描述 説明" in metadata
  57. identifier `Γαμα` mixes scripts: Greek + Latin (in string/comment) (raw pass, confidence 0.40)

        assert 'Author-email: "John X. Ãørçeč"' in metadata
        assert "Γαμα קּ 東 " in metadata
        assert "UTF-8 描述 説明" in metadata
  58. info critical → info by review dynamic-import setuptools/tests/test_bdist_wheel.py:638:16

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

    
            return importlib.__import__(name, *args, **kwargs)
    
        with suppress(KeyError):
    likely benign LLM, confidence 0.90 Test shim that delegates to importlib.__import__ to intercept imports during a test.
    Reasoning

    This is a test helper that wraps importlib.__import__ to inject behavior or record imports during a test. The file is in tests, and the call is a standard mocking pattern for import behavior, not a production dropper.

  59. info warn → info by review dynamic-execution setuptools/wheel.py:212:64

    `eval` called on a non-literal expression (ast pass, confidence 0.70)

                        raw_req,
                        (req for req in reqs if for_extra(req) and eval(req, extra=extra)),
                    )
                )
    likely benign LLM, confidence 0.80 Evaluates environment marker expressions to filter requirements for a given extra.
    Reasoning

    eval is applied to requirement marker strings with an extra parameter, which matches the environment-marker evaluation used when filtering requirements for extras. The eval is on package metadata from the project's own requirements rather than a fetched payload, though the non-literal eval is worth noting.

Reproduce This Scan

Run disclude locally to verify. Add --format sarif for SARIF output. Add --llm (with an API key in the environment) to include the LLM review.

cargo install disclude --version 4.0.0
git clone https://github.com/pypa/setuptools setuptools && cd setuptools
git checkout v84.0.0
disclude scan --severity info --diff v83.0.0 .