Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

PREVIEW: Define the supported rcParams as code#26088

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Draft
timhoffm wants to merge1 commit intomatplotlib:main
base:main
Choose a base branch
Loading
fromtimhoffm:config-definition
Draft
Show file tree
Hide file tree
Changes fromall 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
131 changes: 131 additions & 0 deletionslib/matplotlib/rcsetup.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,11 +14,14 @@
"""

import ast
from dataclasses import dataclass
from functools import lru_cache, reduce
from numbers import Real
import operator
import os
import re
import textwrap
from typing import Any, Callable

import numpy as np

Expand DownExpand Up@@ -1290,3 +1293,131 @@ def _convert_validator_spec(key, conv):
}
_validators = {k: _convert_validator_spec(k, conv)
for k, conv in _validators.items()}


@dataclass
class Param:
name: str
default: Any
validator: Callable[[Any], Any]
desc: str = None

def to_matplotlibrc(self):
return f"{self.name}: {self.default} # {self.desc}\n"


class Comment:
def __init__(self, text):
self.text = textwrap.dedent(text).strip("\n")

def to_matplotlibrc(self):
return self.text + "\n"


class RcParamsDefinition:
"""
Definition of config parameters.

Parameters
----------
content: list of Param and Comment objects
This contains:

- Param objects specifying config parameters
- Comment objects specifying comments to be included in the generated
matplotlibrc file
"""

def __init__(self, content):
self._content = content
self._params = {item.name: item for item in content if isinstance(item, Param)}

def create_default_rcparams(self):
"""
Return a RcParams object with the default values.

Note: The result is equivalent to ``matplotlib.rcParamsDefault``, but
generated from this definition and not from a matplotlibrc file.
"""
from matplotlib import RcParams # TODO: avoid circular import
return RcParams({
name: param.default for name, param in self._params.items()
})

def write_default_matplotlibrc(self, filename):
"""
Write the default matplotlibrc file.

Note: This aspires to fully generate lib/matplotlib/mpl-data/matplotlibrc.
"""
with open(filename, "w") as f:
for item in self._content:
f.write(item.to_matplotlibrc())

def read_matplotlibrc(self, filename):
"""
Read a matplotlibrc file and return a dict with the values.

Note: This is the core of file-reading.
``RcParams(read_matplotlibrc(filename))`` is equivalent to
`matplotlib._rc_params_in_file` (modulo handling of the
additional parameters.

Also note that we have the validation in here, and currently
again in RcParams itself. The validators are currently necessary
to transform the string representation of the values into their
actual type. We may split transformation and validation into
separate steps in the future.
"""
params = {}
with open(filename) as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
name, value = line.split(":", 1)
name = name.strip()
value = value.split("#", 1)[0].strip()
try:
param_def = self._params[name]
except KeyError:
raise ValueError(f"Unknown rcParam {name!r} in {filename!r}")
else:
params[name] = param_def.validator(value)
return params


# This is how the valid rcParams will be defined in the future.
# The Comment sections are a bit ad-hoc, but are necessary for now if we want the
# definition to be the leading source of truth and also be able to generate the
# current matplotlibrc file from it.
rc_def = RcParamsDefinition([
Comment("""
# ***************************************************************************
# * LINES *
# ***************************************************************************
# See https://matplotlib.org/stable/api/artist_api.html#module-matplotlib.lines
# for more information on line properties.
"""),
Param("lines.linewidth", 1.5, validate_float, "line width in points"),
Param("lines.linestyle", "-", validate_string, "solid line"),
Param("lines.color", "C0", validate_string, "has no affect on plot(); see axes.prop_cycle"),
Param("lines.marker", "None", validate_string, "the default marker"),
Param("lines.markerfacecolor", "auto", validate_string, "the default marker face color"),
Param("lines.markeredgecolor", "auto", validate_string, "the default marker edge color"),
Param("lines.markeredgewidth", 1.0, validate_float, "the line width around the marker symbol"),
Param("lines.markersize", 6, validate_float, "marker size, in points"),
Param("lines.dash_joinstyle", "round", validate_string, "{miter, round, bevel}"),
Param("lines.dash_capstyle", "butt", validate_string, "{butt, round, projecting}"),
Param("lines.solid_joinstyle", "round", validate_string, "{miter, round, bevel}"),
Param("lines.solid_capstyle", "projecting", validate_string, "{butt, round, projecting}"),
Comment on lines +1410 to +1413
Copy link
Member

@story645story645Jun 8, 2023
edited
Loading

Choose a reason for hiding this comment

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

This is why I'm biased towards doing type based validation - Param is of type capstyle so it gets validated by a function that lives in/as part of a capstyle type definition. That way it's consistent here and throughout the library, rather than here where you're defining the set of valid styles twice. Honestly I'd even prefer something as basic as a lines.capstyles constant that holds the set.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I expect this to be a larger project with multiple development steps, possibly over multple PRs. As a first step here, I'm only consolidating the existing structure into a single place. Changing the validation logic at the same time would be too much.
Rethinking validation is on the plan and will become easier once this first step is taken.

story645 reacted with thumbs up emoji
Copy link
Member

Choose a reason for hiding this comment

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

Can there be comments marking each section?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Sure.

story645 reacted with thumbs up emoji
Param("lines.antialiased", True, validate_bool, "render lines in antialiased (no jaggies)"),
Comment("""

# The three standard dash patterns. These are scaled by the linewidth.
"""),
Param("lines.dashed_pattern", [3.7, 1.6], validate_floatlist),
Param("lines.dashdot_pattern", [6.4, 1.6, 1, 1.6], validate_floatlist),
Param("lines.dotted_pattern", [1, 1.65], validate_floatlist),
Param("lines.scale_dashes", True, validate_bool),
])
81 changes: 80 additions & 1 deletionlib/matplotlib/tests/test_rcparams.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import copy
import os
import textwrap
from pathlib import Path
import re
import subprocess
Expand DownExpand Up@@ -27,9 +28,13 @@
validate_hist_bins,
validate_int,
validate_markevery,
validate_string,
validate_stringlist,
_validate_linestyle,
_listify_validator)
_listify_validator,
RcParamsDefinition,
Param, Comment,
)


def test_rcparams(tmpdir):
Expand DownExpand Up@@ -604,3 +609,77 @@ def test_rcparams_legend_loc():
match_str = f"{value} is not a valid value for legend.loc;"
with pytest.raises(ValueError, match=re.escape(match_str)):
mpl.RcParams({'legend.loc': value})


class TestRcParamsDefinition:

# sample definition for testing
rc_def = RcParamsDefinition([
Comment("""\
# ***************************************************************************
# * LINES *
# ***************************************************************************
# See https://matplotlib.org/stable/api/artist_api.html#module-matplotlib.lines
# for more information on line properties.
"""),
Param("lines.linewidth", 1.5, validate_float, "line width in points"),
Param("lines.linestyle", "-", validate_string, "solid line"),
Param("lines.antialiased", True, validate_bool, "antialiasing on lines"),
])

@staticmethod
def unindented(text):
"""Helper function to be able to use indented multi-line strings."""
return textwrap.dedent(text).lstrip()

def test_create_default_rcparams(self):
params = self.rc_def.create_default_rcparams()
assert type(params) is mpl.RcParams
# TODO: check why rcParams.items() returns the elements in an order
# different from the order given at setup - for now, just sort
assert sorted(params.items()) == sorted([
("lines.linewidth", 1.5),
("lines.linestyle", "-"),
("lines.antialiased", True),
])

def test_write_default_matplotlibrc(self, tmp_path):
self.rc_def.write_default_matplotlibrc(tmp_path / "matplotlibrc")

lines = (tmp_path / "matplotlibrc").read_text()
assert lines == self.unindented("""
# ***************************************************************************
# * LINES *
# ***************************************************************************
# See https://matplotlib.org/stable/api/artist_api.html#module-matplotlib.lines
# for more information on line properties.
lines.linewidth: 1.5 # line width in points
lines.linestyle: - # solid line
lines.antialiased: True # antialiasing on lines
""")

def test_read_matplotlibrc(self, tmp_path):
(tmp_path / "matplotlibrc").write_text(self.unindented("""
lines.linewidth: 2 # line width in points
lines.linestyle: --
"""))
params = self.rc_def.read_matplotlibrc(tmp_path / "matplotlibrc")
assert params == {
"lines.linewidth": 2,
"lines.linestyle": "--",
}

def test_read_matplotlibrc_unknown_key(self, tmp_path):
(tmp_path / "matplotlibrc").write_text(self.unindented("""
lines.linewidth: 2 # line width in points
lines.unkown_param: "fail"
"""))
with pytest.raises(ValueError, match="Unknown rcParam 'lines.unkown_param'"):
self.rc_def.read_matplotlibrc(tmp_path / "matplotlibrc")

def test_read_matplotlibrc_invalid_value(self, tmp_path):
(tmp_path / "matplotlibrc").write_text(self.unindented("""
lines.linewidth: two # line width in points
"""))
with pytest.raises(ValueError, match="Could not convert 'two' to float"):
self.rc_def.read_matplotlibrc(tmp_path / "matplotlibrc")

[8]ページ先頭

©2009-2025 Movatter.jp