Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ updates:
directory: "/"
schedule:
interval: "weekly"
cooldown:
default-days: 7
groups:
actions:
patterns:
Expand All @@ -13,9 +15,13 @@ updates:
directory: "/"
schedule:
interval: "weekly"
cooldown:
default-days: 7
exclude-paths:
- "ci/minver-requirements.txt"
- package-ecosystem: "pre-commit"
directory: "/"
schedule:
interval: "monthly"
cooldown:
default-days: 7
5 changes: 2 additions & 3 deletions .github/workflows/autoclose_schedule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.13'
- name: Install PyGithub
run: pip install -Uq PyGithub

- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Close PRs labeled more than 14 days ago
run: |
python tools/autoclose_prs.py
24 changes: 24 additions & 0 deletions .github/workflows/linting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,30 @@ jobs:
-tee -reporter=github-check -filter-mode nofilter


clang-tidy:
name: clang-tidy (macOS / Objective-C)
runs-on: macos-latest # run on macOS so we can lint the objectiveC file
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Set up Python 3
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.14'

- name: Install OS dependencies
run: brew install llvm

- name: Install build dependencies
run: pip3 install meson ninja pybind11 setuptools-scm

- name: Run clang-tidy
run: python tools/run_clang_tidy.py

eslint:
name: eslint
runs-on: ubuntu-latest
Expand Down
34 changes: 34 additions & 0 deletions .github/workflows/zizmor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
name: zizmor

on:
push:
branches: [main, v*.x]
pull_request:
branches: [main]
schedule:
- cron: '45 19 * * 1'

permissions: {}

jobs:
zizmor:
name: zizmor
if: github.repository == 'matplotlib/matplotlib'
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Run zizmor
uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3
with:
advanced-security: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19 changes: 19 additions & 0 deletions .github/zizmor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
rules:
dangerous-triggers:
ignore:
# These workflows use pull_request_target solely to obtain write access
# for API operations (labeling, commenting) on fork PRs. None of them
# check out or execute any PR-supplied code.
- autoclose_comment.yml:11
- conflictcheck.yml:3
- labeler.yml:3
- pr_welcome.yml:4
cache-poisoning:
ignore:
# cygwin.yml is a test-only workflow; no artifacts are published.
# The three caches (pip, ccache, matplotlib data) are purely for build
# acceleration and present no poisoning risk for released artifacts.
- cygwin.yml:144:9
- cygwin.yml:151:9
- cygwin.yml:158:9
21 changes: 21 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,27 @@ repos:
hooks:
- id: yamllint
args: ["--strict", "--config-file=.yamllint.yml"]
- repo: https://github.com/nbQA-dev/nbQA
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused why this is needed; https://github.com/astral-sh/ruff-pre-commit has a note on how not to run on notebooks, so I thought it's enabled by default.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, that may explain why it found no problems out of the gate.

rev: d31b7eae1767c43460afb3ba130e0a6602933abe # frozen: 1.9.1
hooks:
- id: nbqa-ruff
args: ["--ignore=E402"]
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: 745eface02aef23e168a8afb6b5737818efbea95 # frozen: v0.11.0.1
hooks:
- id: shellcheck
- repo: https://github.com/zizmorcore/zizmor-pre-commit
rev: a4727cbbcd26d7098e96b9cb738169b59711ae51 # frozen: v1.24.1
hooks:
- id: zizmor
- repo: https://github.com/simple-icons/svglint
rev: 8402586b94f073686e46707a163082e270ee5768 # frozen: v4.2.1
hooks:
- id: svglint
# Override the top-level exclude so that mpl-data/images/ toolbar
# icons are also linted. Exemptions for the intentional interactive
# SVG examples are handled in .svglintrc.mjs.
exclude: '^$'
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 13614ab716a3113145f1294ed259d9fbe5678ff3 # frozen: 0.37.1
hooks:
Expand Down
56 changes: 56 additions & 0 deletions .svglintrc.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/** @type {import('svglint').Config} */
const config = {
rules: {
// Ensure all SVGs are valid XML.
valid: true,

// Block elements that can execute code or embed arbitrary content.
// <script> can run arbitrary JavaScript; <foreignObject> and <iframe>
// can embed arbitrary HTML. Unlike event-handler attributes (which
// are a legitimate tool for matplotlib's interactive SVG examples),
// there is no use case in this repository for these elements outside
// of a <script> already paired with its own exemption.
elm: {
"script": false,
"foreignObject": false,
"iframe": false,
},

custom: [
// Block external URL references in href / xlink:href.
// Internal fragment references (#id), data: URIs, and relative
// paths are all fine. http/https/ftp and protocol-relative URLs
// are blocked because they cause the SVG renderer to make an
// outbound network request, leaking the viewer's IP and UA to an
// attacker-controlled server.
(reporter, $, _ast) => {
reporter.name = "no-external-references";
const externalPattern = /^(https?:|ftp:|\/\/)/i;
$("[href], [xlink\\:href]").each((_i, el) => {
if (!el.attribs) { return; }
const href =
el.attribs["href"] ?? el.attribs["xlink:href"];
if (href && externalPattern.test(href)) {
reporter.error(
`Found external reference '${href}' on <${el.name}>. ` +
"External URL references in SVGs cause the renderer " +
"to make an outbound request, leaking viewer IP/UA."
);
}
});
},
],
},

// These four files are intentional interactive SVG examples that
// demonstrate matplotlib's SVG interactivity features. They contain
// embedded ECMAScript by design and are exempted from the <script> rule.
ignore: [
"doc/_static/svg_histogram.svg",
"doc/_static/svg_tooltip.svg",
"galleries/examples/user_interfaces/images/svg_histogram.svg",
"galleries/examples/user_interfaces/images/svg_tooltip.svg",
],
};

export default config;
35 changes: 35 additions & 0 deletions doc/devel/coding_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,41 @@ C/C++ extensions
implement new features only if the required changes cannot be made elsewhere
in the codebase. In particular, avoid making style fixes to it.

.. _clang-tidy:

Static analysis with clang-tidy
--------------------------------

Matplotlib's C/C++ sources in :file:`src/` are checked with
`clang-tidy <https://clang.llvm.org/extra/clang-tidy/>`__ in CI (see
:file:`.github/workflows/linting.yml`). The check
configuration lives in :file:`src/.clang-tidy`.

The logic lives in :file:`tools/run_clang_tidy.py`, a pure-Python script that
runs on Linux, macOS, and Windows. It requires ``clang-tidy`` on ``PATH``.

The script uses a dedicated ``build/clang-tidy/`` directory for generating
``compile_commands.json`` via ``meson setup`` and requires
``meson``, ``ninja``, and ``pybind11`` when run. To run locally:

.. code-block:: bash

# Run on all files
python tools/run_clang_tidy.py

# Run an a single file
python tools/run_clang_tidy.py src/ft2font.cpp


To suppress false-positives use narrow checks and a comment:

.. code-block:: c++

*indices++ = value; // NOLINT(clang-analyzer-security.ArrayBound): loop
// iterates exactly N times; the analyzer cannot prove this from the macro.



.. _keyword-argument-processing:

Keyword argument processing
Expand Down
36 changes: 36 additions & 0 deletions src/.clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
# clang-tidy configuration for matplotlib's src/ directory.
#
# Philosophy: enable checks that find real bugs (memory safety, undefined
# behaviour, security) and suppress checks that are high-noise style rules
# inappropriate for a C/C++ codebase that interfaces heavily with C APIs
# (CPython, FreeType, libagg) via pybind11.
#
# Run with:
# clang-tidy -p <build_dir> --config-file=src/.clang-tidy <file>

Checks: >
bugprone-*,
clang-analyzer-*,
objc-*,
performance-move-const-arg,
performance-move-constructor-init,
performance-no-automatic-move,
portability-*,
-bugprone-assignment-in-if-condition,
-bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-macro-parentheses,
-bugprone-narrowing-conversions,
-bugprone-reserved-identifier,
-bugprone-throwing-static-initialization,
-clang-analyzer-optin.cplusplus.UninitializedObject,
-clang-analyzer-optin.performance.Padding,

# Only report findings in matplotlib's own src/ headers, not in pybind11,
# Python.h, agg, or other vendored includes.
HeaderFilterRegex: '.*/matplotlib/src/.*'

WarningsAsErrors: ''

CheckOptions: []
Loading
Loading