PyPI / matplotlib / 3.11.2

matplotlib 3.11.2

Source
https://github.com/matplotlib/matplotlib
Artifact scanned
matplotlib-3.11.2.tar.gz (sdist · 31.1 MB · SHA-256 cec596316640…)
Compared with
matplotlib-3.11.1.tar.gz (0 signals introduced since)
Files
1718 scanned, 2829 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 37 info
Scan time
26.1 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 32 of 32 warn and critical signals; 32 demoted to info.
Disclude version
4.3.0
Scanned (UTC)
2026-10-23 08:03

Signals

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

  1. info warn → info by review dynamic-execution .devcontainer/setup.sh:5:1

    command name is a variable (`$SHELL`) not set in this file — the caller or environment decides what runs (ast pass, confidence 0.70)

    
    "${SHELL}" <(curl -Ls micro.mamba.pm/install.sh) < /dev/null
    
    conda init --all
    likely benign LLM, confidence 0.85 Devcontainer setup runs the micromamba install script from the internet through the user's shell.
    Reasoning

    Piping a remote install script via process substitution into a shell chosen by $SHELL is a common bootstrap pattern for micromamba in a devcontainer; the URL is a known vendor installer, though remote script execution warrants a note.

  2. info critical → info by review path-command-shadow .github/workflows/cygwin.yml:124:60

    [embedded bash] redirect writes to `passwd` — filename matches a system command (PATH hijack setup) (ast pass, confidence 0.88)

    /bin/mkpasswd.exe -c | sed -e "s/$(id -u)/0/" >/etc/passwd
    likely benign LLM, confidence 0.93 Cygwin CI step generates /etc/passwd with mkpasswd for the build user.
    Reasoning

    The redirect writes to /etc/passwd, which is the Cygwin passwd file generated from mkpasswd; the path-command-shadow signal misfires because passwd is a file path, not a PATH hijack. Benign CI setup.

  3. info warn → info by review long-line doc/api/artist_api.rst:14:1

    line length 2658 characters (raw pass, confidence 0.65)

    .. inheritance-diagram:: matplotlib.axes._axes.Axes matplotlib.axes._base._AxesBase matplotlib.axis.Axis matplotlib.axis.Tick matplotlib.axis.XAxis matplotlib.axis.XTick matplotlib.axis.YAxis matplotlib.axis.YTick matplotlib.collections.AsteriskPolygonCollection matplotlib.collections.CircleCollection matplotlib.collec
    dismissed LLM, confidence 0.97 Long autogenerated inheritance-diagram directive in API docs.
    Reasoning

    A long line in an RST inheritance-diagram directive listing class names is ordinary documentation content.

  4. info critical → info by review dynamic-import doc/conf.py:163:13

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

            try:
                __import__(name)
            except ImportError:
                missing.append(names[name])
    likely benign LLM, confidence 0.95 Docs config tries importing optional modules to report missing ones.
    Reasoning

    Sphinx conf checking whether optional modules are importable in a loop over names; the import target comes from a fixed mapping, a standard pattern for missing-dependency reporting.

  5. info warn → info by review unicode-homoglyph doc/release/prev_whats_new/dflt_style_changes.rst:707:25

    identifier `α` contains homoglyph candidates: α (03B1)→a (raw pass, confidence 0.70)

       tick_labels = ['😃', '😎', '😴', '😲', '😻']
       bar_labels = ['א', 'α', '☣', '⌬', 'ℝ']
       y = [1, 4, 9, 16, 25]
       x = range(5)
    dismissed LLM, confidence 0.90 Unicode example labels in release notes documentation.
    Reasoning

    Greek alpha inside a string literal in a documentation example showing unicode labels; it is displayed content, not an identifier spoofing anything.

  6. info warn → info by review unicode-homoglyph doc/release/prev_whats_new/dflt_style_changes.rst:717:37

    identifier `со` contains homoglyph candidates: с (0441)→c, о (043E)→o (raw pass, confidence 0.70)

    
       ax.set_title('Диаграмма со смайликами')
    
    See the `DejaVu Sans PDF sample for full coverage
    dismissed LLM, confidence 0.90 Russian sample title in a release-notes example.
    Reasoning

    Cyrillic text inside a string literal in a docs example about title rendering; it is a sample title, not a code identifier.

  7. line length 1249 characters (raw pass, confidence 0.50)

            <img alt="viridis colormap" title="viridis" style="border: 1px solid #555;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAABACAYAAABsv8+/AAAAFnRFWHRUaXRsZQB2aXJpZGlzIGNvbG9ybWFwrE0mCwAAABx0RVh0RGVzY3JpcHRpb24AdmlyaWRpcyBjb2xvcm1hcAtjl3IAAABKdEVYdEF1dGhvcgBNYXRwbG90bGliIHYzLjQuMHJjMy5wb3N0OS5kZXYwK2czZ
  8. info warn → info by review dynamic-format-string extern/agg24-svn/src/ctrl/agg_slider_ctrl.cpp:165:17

    `sprintf` called with a non-literal format string (format-string bug pattern) (ast pass, confidence 0.80)

                    char buf[256];
                    sprintf(buf, m_label, value());
                    m_text.text(buf);
                }
    likely benign LLM, confidence 0.90 Vendored AGG slider control uses sprintf with a stored format string.
    Reasoning

    Vendored AGG library formats a slider label with the sprintf format from a member; the format is developer-controlled, so this is a code-quality issue rather than malicious intent.

  9. info warn → info by review dynamic-execution galleries/examples/ticks/date_formatters_locators.py:32:36

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

        if locator:
            ax.xaxis.set_major_locator(eval(locator))
            ax.xaxis.set_major_formatter(DateFormatter(fmt))
        else:
    likely benign LLM, confidence 0.85 Date locator example evaluates user-chosen locator expression.
    Reasoning

    Example gallery evaluates locator and formatter strings typed by the user in an interactive demo; the eval is over a fixed set of example inputs from the example itself, but it is still eval of input text, mildly risky only if reused with untrusted input.

  10. info warn → info by review dynamic-execution galleries/examples/ticks/date_formatters_locators.py:35:38

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

        else:
            ax.xaxis.set_major_formatter(eval(formatter))
        ax.text(0.0, 0.2, locator or formatter, transform=ax.transAxes,
                fontsize=14, fontname='Monospace', color='tab:blue')
    likely benign LLM, confidence 0.85 Date formatter example evaluates a formatter expression string.
    Reasoning

    Same demo pattern evaluating a formatter expression string chosen by the example author; no network, no obfuscation.

  11. info warn → info by review dynamic-execution galleries/examples/widgets/textbox.py:35:13

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

        """
        ydata = eval(expression, {'np': np}, {'t': t})
        l.set_ydata(ydata)
        ax.relim()
    likely benign LLM, confidence 0.85 Textbox widget example evaluates a typed expression to plot a curve.
    Reasoning

    Textbox widget example evaluates a typed math expression with restricted namespaces; it is the documented purpose of the example and not malicious, though eval of user text is a known hazard.

  12. identifier `αб` mixes scripts: Greek + Cyrillic (in string/comment) (raw pass, confidence 0.40)

    axd = plt.figure(layout="constrained").subplot_mosaic(
        """αб
           ℝ☢"""
    )
  13. info warn → info by review dynamic-execution galleries/users_explain/figure/interactive_guide.rst:443:12

    [markup prose] `eval` called on a string literal (ast pass, confidence 0.85)

               eval(inp)                  # Evaluate / Print
    dismissed LLM, confidence 0.90 Documentation illustration of an input-evaluate loop.
    Reasoning

    eval inside an RST code illustration of an input loop in prose documentation; it is explanatory text, not executed code.

  14. info warn → info by review frame-introspection lib/matplotlib/_api/__init__.py:480:17

    `sys._getframe` — call-stack frame introspection (ast pass, confidence 0.70)

        else:
            frame = sys._getframe()
            for stacklevel in itertools.count(1):
                if frame is None:
    likely benign LLM, confidence 0.93 Matplotlib API helper uses frame introspection to locate caller for warnings.
    Reasoning

    Matplotlib's API helper walks the call stack with sys._getframe to find the caller for deprecation warnings; a standard legitimate use.

  15. info warn → info by review frame-introspection lib/matplotlib/axes/_base.py:266:40

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

                if pos_only in kwargs:
                    raise _api.kwarg_error(inspect.stack()[1].function, pos_only)
    
            if not args:
    likely benign LLM, confidence 0.93 Axes code uses inspect.stack to build a keyword-argument error.
    Reasoning

    inspect.stack used to name the calling function in a keyword error message; ordinary library behavior.

  16. info warn → info by review long-line lib/matplotlib/backends/web_backend/js/mpl.js:732:1

    line length 2004 characters (raw pass, confidence 0.65)

    var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError("Constructor requires 'new' operator");i.set(this,e)}function h(){throw new TypeError("Function is not a constructor")}function c(e,t,i,n){e=0 in ar
    likely benign LLM, confidence 0.85 Minified vendored resize-observer polyfill in the web backend.
    Reasoning

    A minified bundled JavaScript resize observer polyfill (jsxtools) on one long line; minification explains the length, and it is a known vendored library rather than hidden payload.

  17. info warn → info by review dynamic-attribute lib/matplotlib/layout_engine.py:212:16

    reach-by-name through `locals` (ast pass, confidence 0.60)

            for td in self.set.__kwdefaults__:
                if locals()[td] is not None:
                    self._params[td] = locals()[td]
    likely benign LLM, confidence 0.88 Layout engine copies constructor arguments using locals().
    Reasoning

    Reads local variables by name via locals() to collect constructor parameters; a clumsy but benign reflection pattern over a fixed set of keyword names.

  18. info warn → info by review dynamic-attribute lib/matplotlib/layout_engine.py:213:36

    reach-by-name through `locals` (ast pass, confidence 0.60)

                if locals()[td] is not None:
                    self._params[td] = locals()[td]
    likely benign LLM, confidence 0.90 Copies named function parameters into a params dict via locals().
    Reasoning

    locals() lookup by parameter name to copy keyword arguments into self._params is a standard matplotlib idiom for reading function arguments; no external input or payload is involved.

  19. info warn → info by review dynamic-attribute lib/matplotlib/layout_engine.py:308:16

    reach-by-name through `locals` (ast pass, confidence 0.60)

            for td in self.set.__kwdefaults__:
                if locals()[td] is not None:
                    self._params[td] = locals()[td]
    likely benign LLM, confidence 0.90 Iterates keyword-only defaults and reads matching locals into _params.
    Reasoning

    Same locals() pattern iterating over keyword-only defaults of a known method; benign parameter reflection.

  20. info warn → info by review dynamic-attribute lib/matplotlib/layout_engine.py:309:36

    reach-by-name through `locals` (ast pass, confidence 0.60)

                if locals()[td] is not None:
                    self._params[td] = locals()[td]
    likely benign LLM, confidence 0.90 Reads locals by name to store layout parameters.
    Reasoning

    Duplicate of the same benign locals() parameter-copy idiom in a sibling method.

  21. info unicode-homoglyph lib/matplotlib/path.py:1131:36

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

        method as for collections: each is iterated over independently, so if you have 3
        paths (A, B, C), 2 transforms (α, β) and 1 offset (O), their combinations are as
        follows:
  22. info unicode-homoglyph lib/matplotlib/path.py:1134:11

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

    
        - (A, α, O)
        - (B, β, O)
        - (C, α, O)
  23. info unicode-homoglyph lib/matplotlib/path.py:1136:11

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

        - (B, β, O)
        - (C, α, O)
        """
        from .transforms import Bbox
  24. info warn → info by review dynamic-attribute lib/matplotlib/pyplot.py:535:9

    reach-by-name through `globals` (ast pass, confidence 0.60)

        for func_name in ["new_figure_manager", "draw_if_interactive", "show"]:
            globals()[func_name].__signature__ = inspect.signature(
                getattr(backend_mod, func_name))
    likely benign LLM, confidence 0.90 Copies backend function signatures onto pyplot globals for a fixed set of names.
    Reasoning

    Loop over a fixed literal list of backend function names assigning __signature__ onto module globals; standard for mirroring backend signatures into pyplot.

  25. info warn → info by review dynamic-execution lib/matplotlib/sphinxext/plot_directive.py:368:9

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

            # check if it's valid Python as-is
            compile(text, '<string>', 'exec')
            return False
        except SyntaxError:
    likely benign LLM, confidence 0.85 Compiles plot source text only to test whether it is valid Python.
    Reasoning

    compile of documentation source text to check syntax validity is a normal sphinx extension check, not a payload execution.

  26. info warn → info by review dynamic-execution lib/matplotlib/sphinxext/plot_directive.py:588:21

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

                    else:
                        exec(str(setup.config.plot_pre_code), ns)
                if "__main__" in code:
                    ns['__name__'] = '__main__'
    likely benign LLM, confidence 0.80 Executes the configured pre-code for documentation plots.
    Reasoning

    exec of a plot_pre_code config value set by the documentation author is the intended behavior of the plot directive, running author-supplied doc code in a build context.

  27. info warn → info by review dynamic-execution lib/matplotlib/sphinxext/plot_directive.py:594:17

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

                with cbook._setattr_cm(FigureManagerBase, show=lambda self: None):
                    exec(code, ns)
                    if function_name is not None:
                        exec(function_name + "()", ns)
    likely benign LLM, confidence 0.80 Executes plot directive source code to generate figures.
    Reasoning

    exec of documentation plot source inside the Sphinx build is the core purpose of the plot directive; the code comes from the project's own docs.

  28. info critical → info by review dynamic-execution lib/matplotlib/sphinxext/plot_directive.py:596:21

    `exec` called on a constructed string (ast pass, confidence 0.80)

                    if function_name is not None:
                        exec(function_name + "()", ns)
    
            except (Exception, SystemExit) as err:
    likely benign LLM, confidence 0.75 Calls a named plotting function from the directive option within the doc namespace.
    Reasoning

    exec of function_name plus empty parentheses is a constructed call, but function_name comes from the directive's own option in documentation source; it only invokes a named function in the plot namespace, consistent with plot directive behavior.

  29. info warn → info by review decoder-decompress-payload lib/matplotlib/tests/test_simplification.py:449:27

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

    
        verts = np.frombuffer(base64.decodebytes(data), dtype='<i4')
        verts = verts.reshape((len(verts) // 2, 2))
        path = Path(verts)
    likely benign LLM, confidence 0.85 Decodes base64 test vertex data into a numpy array.
    Reasoning

    base64 decoding of a test fixture into an int32 vertex array is ordinary test data handling; it produces a numpy array passed to Path, not executed code.

  30. info warn → info by review dynamic-execution lib/matplotlib/tests/test_transforms.py:879:23

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

        b = mtransforms.Bbox([[.5, 0], [.75, .75]])
        assert_bbox_eq(b, eval(repr(b), {'Bbox': mtransforms.Bbox}))
        asdict = eval(str(b), {'Bbox': dict})
        for k, v in asdict.items():
    likely benign LLM, confidence 0.90 Evaluates the repr of a Bbox in a test round-trip check.
    Reasoning

    eval of repr of a Bbox with a restricted namespace is a round-trip serialization test; inputs are internally generated.

  31. info warn → info by review dynamic-execution lib/matplotlib/tests/test_transforms.py:880:14

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

        assert_bbox_eq(b, eval(repr(b), {'Bbox': mtransforms.Bbox}))
        asdict = eval(str(b), {'Bbox': dict})
        for k, v in asdict.items():
            assert getattr(b, k) == v
    likely benign LLM, confidence 0.90 Evaluates str of a Bbox to check formatted output.
    Reasoning

    eval of str(b) with a restricted namespace in a test asserting formatting; internally generated, benign.

  32. info warn → info by review dynamic-execution lib/matplotlib/tests/test_transforms.py:884:14

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

        fmt = '.1f'
        asdict = eval(format(b, fmt), {'Bbox': dict})
        for k, v in asdict.items():
            assert eval(format(getattr(b, k), fmt)) == v
    likely benign LLM, confidence 0.90 Evaluates a formatted Bbox string in a test.
    Reasoning

    eval of a format string of the Bbox in a test with a fixed format spec; benign test code.

  33. info warn → info by review dynamic-execution lib/matplotlib/tests/test_transforms.py:886:16

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

        for k, v in asdict.items():
            assert eval(format(getattr(b, k), fmt)) == v
    likely benign LLM, confidence 0.90 Evaluates formatted numeric attributes for comparison in a test.
    Reasoning

    eval of formatted numeric attribute values in a test assertion; benign.

  34. info warn → info by review identifier-confusable-collision src/_path.h:395:20

    two identifiers collapse to visual skeleton `xo` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

            if (Noffsets) {
                double xo = offsets(i % Noffsets, 0);
                double yo = offsets(i % Noffsets, 1);
                offset_trans.transform(&xo, &yo);
    likely benign LLM, confidence 0.85 Local double variable xo for an x offset coordinate.
    Reasoning

    xo and yo are ordinary coordinate variable names; the confusable collision is an artifact of the skeleton heuristic on short names, not homoglyph obfuscation, and the code is standard offset transform logic.

  35. info warn → info by review identifier-confusable-collision src/_path.h:396:20

    two identifiers collapse to visual skeleton `yo` (0/O/o and 1/l/I are confusable) (token pass, confidence 0.80)

                double xo = offsets(i % Noffsets, 0);
                double yo = offsets(i % Noffsets, 1);
                offset_trans.transform(&xo, &yo);
                trans *= agg::trans_affine_translation(xo, yo);
    likely benign LLM, confidence 0.85 Local double variable yo for a y offset coordinate.
    Reasoning

    yo is the matching y-offset coordinate variable in standard transform code; the flagged collision is a false positive from the skeleton heuristic.

  36. info warn → info by review dynamic-import src/_tkagg.cpp:315:25

    `dlopen` called with a non-literal path (ast pass, confidence 0.75)

        // Try loading from the main program namespace first.
        auto main_program = dlopen(nullptr, RTLD_LAZY);
        auto success = load_tcl_tk(main_program);
        // We don't need to keep a reference open as the main program always exists.
    likely benign LLM, confidence 0.93 Obtains a handle to the main program so Tcl/Tk symbols already loaded into the interpreter can be resolved.
    Reasoning

    dlopen(nullptr, RTLD_LAZY) returns a handle to the main program's global symbol namespace, not an arbitrary library path. The code then looks up Tcl/Tk symbols in it, and the comment explains the intent. This is a standard way to find Tcl/Tk symbols already linked into the Python process. No attacker-controlled path or payload is involved.

  37. info warn → info by review dynamic-import src/_tkagg.cpp:336:24

    `dlopen` called with a non-literal path (ast pass, confidence 0.75)

        std::string path = py_path_b;
        auto tkinter_lib = dlopen(path.c_str(), RTLD_LAZY);
        if (!tkinter_lib) {
            throw std::runtime_error(dlerror());
    likely benign LLM, confidence 0.90 Loads the tkinter shared library from the path Python reports for its tkinter module, so the matching Tcl/Tk libraries can be used.
    Reasoning

    The path comes from py_path_b, which the surrounding code takes from Python's tkinter module location, and it is used to load the tkinter shared library that Python itself uses. The non-literal path is needed because the library's location varies by install. The failure path throws a runtime error rather than continuing silently. This matches matplotlib's Tk backend loading logic.

Reproduce This Scan

Run disclude locally to verify. These commands scan the published file; the "new in this release" marks come from comparing it with the previous release's file, which they don't repeat. 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.3.0
curl -fsSL -o matplotlib-3.11.2.tar.gz https://files.pythonhosted.org/packages/e7/c8/9aa712a0afb882649424dd8de8ad9aa6235e796e84c6052e8f6dc1598d0d/matplotlib-3.11.2.tar.gz
printf '%s  %s\n' cec596316640f2b394b8f0daa0ea61a8eae82d017b620b9f202befb972a59ea4 matplotlib-3.11.2.tar.gz | shasum -a 256 -c -
mkdir pkg && tar -xzf matplotlib-3.11.2.tar.gz -C pkg --strip-components 1
disclude scan --untrusted --severity info pkg