Skip to content

Conversation

@timhoffm
Copy link
Member

@timhoffm timhoffm commented Dec 16, 2025

Current status
Until now, the parameter definition was dispersed: valid names and defaults are loaded from the canonical matplotlibrc data file. Docs are only available as comments in there. Validators are attached ad-hoc in rcsetup.py.

Proposed change
Define the parameters in code, including meta-information, like validators, docs in a single place. It will simplify the rcParams related code in matplotlib.__init__.py and matplotlib.rcsetup.py. It also enables us to generate sphinx documentation on the parameters.

Closes #23531.
Superseeds #26088. Ensuring consistency through tests is a better approach than trying to be able to exactly reproduce matplotlibrc.

Discarded alternative: Use another stuctured data file to store the information. Among available formats, I would only find YAML easy enough to human-read and -write this kind of structured information. But I don't want to introduce a YAML parser as a dependency.

Notes:

  • This is a PoC and nothing is set in stone. In particular no special care has been taken of
    • The exact structures how we want to define the parameters (is a dataclass good? Likely yes, but ymmv) and format them so that they are as readable as possible.
    • Description of the parameters. These are just extracted from matplotlibrc
    • More fancy stuff like grouping, additional info like human-readable accepted values may come later.
    • Representation in the docs. Generating and embedding a simple rst page will not be the final state. My plan is to make a proper sphinx extension so that :rc:some.param can directly line the parameters.
    • Location of the params documentation in the docs.
  • Transition plan:
    1. matplotlibrc and the validator lists are used for functionality. Tests ensure that the new definitions match the existing ones.
    2. After we have merged, a later PR will switch to using the content from the new definitions and remove matplotlibrc and the validator lists.

How to review:

Feedback welcome.

@story645
Copy link
Member

How can we improve on that?

This definitely looks better than what's currently there. I think subheadings based on the artist/object that the rcParam applies to - like #lines, which will also make the right hand side really nice for quick skims I think. I think the two ways to achieve that are probably either manually add a family or peel off the family with a .split?

@timhoffm
Copy link
Member Author

timhoffm commented Dec 17, 2025

Alternative: It would likely be feasible to define the rcParams via a TOML file, which we would carry around and read at startup like we do with matplotlibrc.

Compare:

Param("timezone", "UTC", validate_string,
    description="a pytz timezone string, e.g., US/Central or Europe/Paris"),
Param("pcolor.shading", "auto", ["auto", "flat", "nearest", "gouraud"]),
Param("figure.constrained_layout.hspace", 0.02, validate_float,
    description="Spacing between subplots, relative to the subplot sizes.  Much "
                "smaller than for tight_layout (figure.subplot.hspace, "
                "figure.subplot.wspace) as constrained_layout already takes "
                "surrounding texts (titles, labels, # ticklabels) into account."),

and:

[timezone]
default = "UTC"
validator = "validate_string"
description ="a pytz timezone string, e.g., US/Central or Europe/Paris"

[pcolor.shading]
default = "auto"
validator = '["auto", "flat", "nearest", "gouraud"]'

[figure.constrained_layout.hspace]
default = 0.02
validator = "validate_float"
description = """
Spacing between subplots, relative to the subplot sizes.  Much
smaller than for tight_layout (figure.subplot.hspace, "
figure.subplot.wspace) as constrained_layout already takes
surrounding texts (titles, labels, # ticklabels) into account.
"""

Which one is easier to maintain?

There'll be slight complications that we need to be able to express None as a default value and that we have to define validators as strings and map them to code objects at runtime, but nothing that cannot be done.

@story645
Copy link
Member

story645 commented Dec 17, 2025

Which one is easier to maintain?

The toml file looks way easier to read/find the right value. And I like that the leveling structure means we can do group level stuff trivially (if I'm reading the TOML docs correctly), eg:

[figure.constrained_layout]
description = "default settings for constrained layout, see <link to constrained layout docs>" 

@tacaswell tacaswell added this to the v3.12.0 milestone Dec 17, 2025
@jklymak
Copy link
Member

jklymak commented Dec 17, 2025

I think it has been suggested for years that the matplotlibrc be a Python file instead of some parsable text format?

@story645
Copy link
Member

story645 commented Dec 17, 2025

@jklymak Tim is proposing a replacement for how rcParams are specified in rcsetup.py. Roughly this replaces the _validators dictionary and the adds in the default values and description from the default rcParams file so that all the info about the param is in one place.
ETA: basically, we should be able to generate a default matplotlibrc file from the specs rather than the current scheme where defaults are defined in the matplotlibrc file.

Separately it might be nice for users to be able to specify params using a TOML structure, but generally they're only going to need to specify the values so the structure here is a bit overkill for that.

@jklymak
Copy link
Member

jklymak commented Dec 17, 2025

Sure I understand that - however, I don't see why we would add a new TOML format to the mix.

@WeatherGod
Copy link
Member

WeatherGod commented Dec 17, 2025 via email

@timhoffm
Copy link
Member Author

Chiming in here with a note about a tool I used once in a work project for schema validation: https://docs.python-cerberus.org/

Thanks for the hint. Though I think schema validation is not our primary concern for now. We have potentially two schemas:

  • the config_parameter_schema, but if we define (or parse) that into a dataclass, errors will show up. Also the input is on our side and rarely changes.
  • user-defind rcParams setttings. These are just key-value pairs, with a potentially complicated (in the sense of allowed values, not in the sense of structure) value type. A schema is not much help here either.

@story645
Copy link
Member

however, I don't see why we would add a new TOML format to the mix.

Because it's a ready made solution to the problem of defining specs for nested configurations, which is more or less what rcParams are. The alternative proposal of wrapping everything in Param objects (or the like) would likely grow to something that looks like TOML in object form if we want to make the groupings explicit, which I think we do because that's really useful for documentation purposes.

@timhoffm
Copy link
Member Author

timhoffm commented Dec 17, 2025

Sure I understand that - however, I don't see why we would add a new TOML format to the mix.

It's a variant how to systematically represent that information. We are completely free to decide which one we like better.

What I like about the TOML approach:

  • The parameter name stands out [param.name], it's visually separated from the other values

  • There's no extra syntactic clutter that we need in Python: no class name, no parentheses, no commas

  • Multiline strings look a bit nicer. This is mainly because your keys are not indented.

    [some.param]
    description="""
    A longer description that spans multiple
    lines.
    """

    In Python you do one of

    implicit concatenation of single lines (needs extra quotes on each line and ensure that there's a space at the end of each line.

        Param(
            description="A longer description that spans multiple "
                        "lines."
        )

    or (visually breaking the indentation)

        Param(
            description="""\
     A longer description that spans multiple
    lines.
    """
        )

    or (adding additional space into the string)

        Param(
            description="""\
                A longer description that spans multiple
                lines.
                """
        )

    Also, depending on the variant, effective line length gets much shorter, which means we need more wrapping.

What I don't like about the TOML approach:

  • Overhead for carrying a file around (but we right now do this with matplotlibrc) so not too bad.
  • No native value for None
  • We must define a pattern how we define validators (e.g. as string) and then parse them into the actual objects on load.

@story645
Copy link
Member

Overhead for carrying a file around (but we right now do this with matplotlibrc) so not too bad.

I think it'd be nice to pull the configuration out of rcsetup even if we don't go the TOML route.

@timhoffm
Copy link
Member Author

Overhead for carrying a file around (but we right now do this with matplotlibrc) so not too bad.

I think it'd be nice to pull the configuration out of rcsetup even if we don't go the TOML route.

I did not do this for a start, because one then needs to import all the validators, and that gets messy. But rearrangement is possible and will happen in some form.

@timhoffm timhoffm force-pushed the config_definition2 branch 2 times, most recently from ea06190 to 0ccad61 Compare December 18, 2025 00:16
@timhoffm
Copy link
Member Author

I've reformatted the parameter definition to one value per line, which makes this a bit more readable. For now, I'd say the code representation is good enough. We can always later consider whether TOML might yield additional advantages.

@timhoffm
Copy link
Member Author

I've added links to so that individual :rc: entries link to their parameter description (not any more to the reproduced default matplotlibrc file.

Overall, I'd say this is ready to go into 3.11 as it is already a massive improvement over the current state. At the same time, the risk is relatively low because the rcParams as code are currently only used for the docs. They are not used for code logic.

The only think that is worth discussing is where to put the list of rcParams in the docs. Is there a better place than galleries/users_explain/customizing.py?

@timhoffm timhoffm force-pushed the config_definition2 branch 2 times, most recently from e207de8 to 9de7838 Compare December 19, 2025 17:46
@story645
Copy link
Member

story645 commented Dec 19, 2025

Is there a better place than galleries/users_explain/customizing.py?

I think they should be on their own page, linked out to from customizing. ETA: mostly b/c they're a standalone reference independent from the customizing guide/tutorial, so it's a clear separation of purpose/scope.

@timhoffm
Copy link
Member Author

Yes, but where should the new page be? Is this also /users/explain/...? It's rather the category "reference", but there we currently only have API documentation.

@story645
Copy link
Member

story645 commented Dec 19, 2025

It's rather the category "reference", but there we currently only have API documentation.

I think it's documenting the rcparams API and would be far better than current docs so I'd put a new reference page under the style module.

@timhoffm
Copy link
Member Author

I'd put a new reference page under the style module.

The style module is not the right place. Styles are realized through rcParams, but rcParams are a more fundamental concept. Conceptually, it belongs in https://matplotlib.org/devdocs/api/rcsetup_api.html, but that whole module is basically not user-facing. The main user-facing functions are all in the top-level matplotlib module, but listing the parameters there is also not in proportion.

I see the following options:

  • leave it in /users/explain/customizing as it is. The scope is also a bit of a stretch here, but at least this is is the place this kind of information has been availble in the past (through the listing of matplotlibrc). Later restructure to a different place.
  • put it under a new page in /users/explain. As said, it's not really "explain" but heh.

The point is that our current docs structure cannot reasonbly accomodate this information. But I don't want to rearchitect the docs structure right now. OTOH I think this is already a valuable improvment, no matter where it lives.

@story645
Copy link
Member

put it under a new page in /users/explain. As said, it's not really "explain" but heh.

On the one hand I'm thinking do this, on the other I'm thinking that it can get it's own page under rc if/when configuration gets pulled out of rcsetup.

@timhoffm
Copy link
Member Author

The advantage of a new page is that we can delete it later and redirect it to another location. That's more difficult with rcsetup.

@story645
Copy link
Member

The advantage of a new page is that we can delete it later and redirect it to another location. That's more difficult with rcsetup.

I was thinking more of avoiding the redirect if down the line it gets its own page in API b/c it gets its own file c/o #30871 (comment), but redirects are cheap enough that I'm also pro new page now in case the rearch doesn't happen

Until now, the parameter definition was dispersed:
valid names and defaults are loaded from the
canonical `matplotlibrc` data file. Docs are only
available as comments in there. Validators are
attached ad-hoc in `rcsetup.py`.

This makes for a more formal definition of parameters,
including meta-information, like validators, docs in
a single place. It will simplify the rcParams related
code in `matplotlib.__init__.py` and `matplotlib.rcsetup.py`.
It also enables us to generate sphinx documentation
on the parameters.
@timhoffm
Copy link
Member Author

This is now ready as a complete minimal update.

See parameter list: https://output.circle-artifacts.com/output/job/76615068-68c0-4ccd-9a2b-a7f52a8ecf0c/artifacts/0/doc/build/html/users/explain/configuration.html
And e.g. this site to check that linking now goes to the parameter: https://output.circle-artifacts.com/output/job/76615068-68c0-4ccd-9a2b-a7f52a8ecf0c/artifacts/0/doc/build/html/api/_as_gen/matplotlib.axes.Axes.scatter.html#matplotlib.axes.Axes.scatter

As already said above:

Overall, I'd say this is ready to go into 3.11 as it is already a massive improvement over the current state. At the same time, the risk is relatively low because the rcParams as code are currently only used for the docs. They are not used for code logic - any changes to that would be 3.12+

Review tips:

  • you don't have to check that all parameters are defined there or have correct defaults or config values. That is covered by the tests
  • parameter descriptions are extracted from matplotlibrc. There’s still lots of room for improvement, but let’s do content updates as separate follow-ups. Likewise with creating subsections for parameter groups.

@timhoffm timhoffm modified the milestones: v3.12.0, v3.11.0 Dec 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Doc]: Documentation of rc parameters could be improved

5 participants