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

Commit2a2e584

Browse files
committed
Simplify _preprocess_data using Signature.bind.
Public API change: `step` no longer defaults to using `y` aslabel_namer. This is consistent with other functions that wrap `plot`(`plot` itself, `loglog`, etc.). (Alternatively, we could make allthese functions use `y` as label_namer; I don't really care either way.)The plot-specific data kwarg logic was moved to`_process_plot_var_args`, dropping the need forgeneral callable `positional_parameter_names`,`_plot_args_replacer`, and `positional_parameter_names`.`test_positional_parameter_names_as_function` and tests using`plot_func_varargs` were removed as a consequence.`replace_all_args` can be replaced by making `replace_names=None`trigger replacement of all args, even the "unknown" ones. There wasno real use of "replace all known args but not unknown ones" (even ifthere was, this can easily be handled by explicitly listing the args inreplace_names). `test_function_call_with_replace_all_args` was removedas a consequence.`replace_names` no longer complains if some argument names it is givenare not present in the "explicit" signature, as long as the functionaccepts `**kwargs` -- because it may find the arguments in kwargsinstead.label_namer no longer triggers if `data` is not passed (if theargument specified by label_namer was a string, then it islikely a categorical and shouldn't be considered as a labelanyways). `test_label_problems_at_runtime` was renamed to`test_label_namer_only_if_data` and modified accordingly.Calling data-replaced functions used to trigger RuntimeError in somecases of mismatched arguments; they now trigger TypeError similarly tohow normal functions do (`test_more_args_than_pos_parameters`).
1 parent9595a7d commit2a2e584

File tree

4 files changed

+185
-426
lines changed

4 files changed

+185
-426
lines changed

‎lib/matplotlib/__init__.py

Lines changed: 109 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,19 +1503,19 @@ def test(verbosity=None, coverage=False, switch_backend_warn=True,
15031503
test.__test__=False# pytest: this function is not a test
15041504

15051505

1506-
def_replacer(data,key):
1506+
def_replacer(data,value):
15071507
"""Either returns data[key] or passes data back. Also
15081508
converts input data to a sequence as needed.
15091509
"""
1510-
# if key isn't a string don't bother
1511-
ifnotisinstance(key,str):
1512-
returnkey
1513-
# try to use __getitem__
15141510
try:
1515-
returnsanitize_sequence(data[key])
1516-
# key does not exist, silently fall back to key
1517-
exceptKeyError:
1518-
returnkey
1511+
# if key isn't a string don't bother
1512+
ifisinstance(value,str):
1513+
# try to use __getitem__
1514+
value=data[value]
1515+
exceptException:
1516+
# key does not exist, silently fall back to key
1517+
pass
1518+
returnsanitize_sequence(value)
15191519

15201520

15211521
_DATA_DOC_APPENDIX="""
@@ -1529,43 +1529,33 @@ def _replacer(data, key):
15291529
"""
15301530

15311531

1532-
def_add_data_doc(docstring,replace_names,replace_all_args):
1532+
def_add_data_doc(docstring,replace_names):
15331533
"""Add documentation for a *data* field to the given docstring.
15341534
15351535
Parameters
15361536
----------
15371537
docstring : str
15381538
The input docstring.
1539-
replace_names :list of strings or None
1539+
replace_names :List[str] or None
15401540
The list of parameter names which arguments should be replaced by
1541-
`data[name]`. If None, all arguments are replaced if they are
1542-
included in `data`.
1543-
replace_all_args : bool
1544-
If True, all arguments in *args get replaced, even if they are not
1545-
in replace_names.
1541+
``data[name]`` (if ``data[name]`` does not throw an exception). If
1542+
None, replacement is attempted for all arguments.
15461543
15471544
Returns
15481545
-------
15491546
The augmented docstring.
15501547
"""
1551-
ifdocstringisNone:
1552-
docstring=''
1553-
else:
1554-
docstring=dedent(docstring)
1555-
_repl=""
1556-
ifreplace_namesisNone:
1557-
_repl="* All positional and all keyword arguments."
1558-
else:
1559-
iflen(replace_names)!=0:
1560-
_repl="* All arguments with the following names: '{names}'."
1561-
ifreplace_all_args:
1562-
_repl+="\n * All positional arguments."
1563-
_repl=_repl.format(names="', '".join(sorted(replace_names)))
1564-
returndocstring+_DATA_DOC_APPENDIX.format(replaced=_repl)
1548+
docstring=dedent(docstring)ifdocstringisnotNoneelse""
1549+
repl= ("* All positional and all keyword arguments."
1550+
ifreplace_namesisNoneelse
1551+
""
1552+
iflen(replace_names)==0else
1553+
"* All arguments with the following names: {}.".format(
1554+
", ".join(map(repr,sorted(replace_names)))))
1555+
returndocstring+_DATA_DOC_APPENDIX.format(replaced=repl)
15651556

15661557

1567-
def_preprocess_data(replace_names=None,replace_all_args=False,
1568-
label_namer=None,positional_parameter_names=None):
1558+
def_preprocess_data(func=None,*,replace_names=None,label_namer=None):
15691559
"""
15701560
A decorator to add a 'data' kwarg to any a function. The signature
15711561
of the input function must include the ax argument at the first position ::
@@ -1576,216 +1566,109 @@ def foo(ax, *args, **kwargs)
15761566
15771567
Parameters
15781568
----------
1579-
replace_names :list of strings, optional, default: None
1569+
replace_names :List[str] or None, optional, default: None
15801570
The list of parameter names which arguments should be replaced by
1581-
`data[name]`. If None, all arguments are replaced if they are
1582-
included in `data`.
1583-
replace_all_args : bool, default: False
1584-
If True, all arguments in *args get replaced, even if they are not
1585-
in replace_names.
1571+
``data[name]`` (if ``data[name]`` does not throw an exception). If
1572+
None, replacement is attempted for all arguments.
15861573
label_namer : string, optional, default: None
15871574
The name of the parameter which argument should be used as label, if
15881575
label is not set. If None, the label keyword argument is not set.
1589-
positional_parameter_names : list of strings or callable, optional
1590-
The full list of positional parameter names (excluding an explicit
1591-
`ax`/'self' argument at the first place and including all possible
1592-
positional parameter in `*args`), in the right order. Can also include
1593-
all other keyword parameter. Only needed if the wrapped function does
1594-
contain `*args` and (replace_names is not None or replace_all_args is
1595-
False). If it is a callable, it will be called with the actual
1596-
tuple of *args and the data and should return a list like
1597-
above.
1598-
NOTE: callables should only be used when the names and order of *args
1599-
can only be determined at runtime. Please use list of names
1600-
when the order and names of *args is clear before runtime!
16011576
16021577
.. note:: decorator also converts MappingView input data to list.
16031578
"""
1604-
ifreplace_namesisnotNone:
1605-
replace_names=set(replace_names)
16061579

1607-
defparam(func):
1608-
sig=inspect.signature(func)
1609-
_has_varargs=False
1610-
_has_varkwargs=False
1611-
_arg_names= []
1612-
params=list(sig.parameters.values())
1613-
forpinparams:
1614-
ifp.kindisParameter.VAR_POSITIONAL:
1615-
_has_varargs=True
1616-
elifp.kindisParameter.VAR_KEYWORD:
1617-
_has_varkwargs=True
1618-
else:
1619-
_arg_names.append(p.name)
1620-
data_param=Parameter('data',Parameter.KEYWORD_ONLY,default=None)
1621-
if_has_varkwargs:
1622-
params.insert(-1,data_param)
1623-
else:
1624-
params.append(data_param)
1625-
new_sig=sig.replace(parameters=params)
1626-
# Import-time check: do we have enough information to replace *args?
1627-
arg_names_at_runtime=False
1628-
# there can't be any positional arguments behind *args and no
1629-
# positional args can end up in **kwargs, so only *varargs make
1630-
# problems.
1631-
# http://stupidpythonideas.blogspot.de/2013/08/arguments-and-parameters.html
1632-
ifnot_has_varargs:
1633-
# all args are "named", so no problem
1634-
# remove the first "ax" / self arg
1635-
arg_names=_arg_names[1:]
1580+
iffuncisNone:
1581+
returnfunctools.partial(
1582+
_preprocess_data,
1583+
replace_names=replace_names,label_namer=label_namer)
1584+
1585+
sig=inspect.signature(func)
1586+
varargs_name=None
1587+
varkwargs_name=None
1588+
arg_names= []
1589+
params=list(sig.parameters.values())
1590+
forpinparams:
1591+
ifp.kindisParameter.VAR_POSITIONAL:
1592+
varargs_name=p.name
1593+
elifp.kindisParameter.VAR_KEYWORD:
1594+
varkwargs_name=p.name
16361595
else:
1637-
# Here we have "unnamed" variables and we need a way to determine
1638-
# whether to replace a arg or not
1639-
ifreplace_namesisNone:
1640-
# all argnames should be replaced
1641-
arg_names=None
1642-
eliflen(replace_names)==0:
1643-
# No argnames should be replaced
1644-
arg_names= []
1645-
eliflen(_arg_names)>1and (positional_parameter_namesisNone):
1646-
# we got no manual parameter names but more than an 'ax' ...
1647-
iflen(replace_names-set(_arg_names[1:]))==0:
1648-
# all to be replaced arguments are in the list
1649-
arg_names=_arg_names[1:]
1650-
else:
1651-
raiseAssertionError(
1652-
"Got unknown 'replace_names' and wrapped function "
1653-
"{!r} uses '*args', need 'positional_parameter_names'"
1654-
.format(func.__name__))
1655-
else:
1656-
ifpositional_parameter_namesisnotNone:
1657-
ifcallable(positional_parameter_names):
1658-
# determined by the function at runtime
1659-
arg_names_at_runtime=True
1660-
# so that we don't compute the label_pos at import time
1661-
arg_names= []
1662-
else:
1663-
arg_names=positional_parameter_names
1664-
else:
1665-
ifreplace_all_args:
1666-
arg_names= []
1667-
else:
1668-
raiseAssertionError(
1669-
"Got 'replace_names' and wrapped function {!r} "
1670-
"uses *args, need 'positional_parameter_names' or "
1671-
"'replace_all_args'".format(func.__name__))
1672-
1673-
# compute the possible label_namer and label position in positional
1674-
# arguments
1675-
label_pos=9999# bigger than all "possible" argument lists
1676-
label_namer_pos=9999# bigger than all "possible" argument lists
1677-
if (label_namerand# we actually want a label here ...
1678-
arg_namesand# and we can determine a label in *args ...
1679-
label_namerinarg_names):# and it is in *args
1680-
label_namer_pos=arg_names.index(label_namer)
1681-
if"label"inarg_names:
1682-
label_pos=arg_names.index("label")
1683-
1684-
# Check the case we know a label_namer but we can't find it the
1685-
# arg_names... Unfortunately the label_namer can be in **kwargs,
1686-
# which we can't detect here and which results in a non-set label
1687-
# which might surprise the user :-(
1688-
iflabel_namerandnotarg_names_at_runtimeandnot_has_varkwargs:
1689-
ifnotarg_names:
1690-
raiseAssertionError(
1691-
"label_namer {!r} can't be found as the parameter without "
1692-
"'positional_parameter_names'".format(label_namer))
1693-
eliflabel_namernotinarg_names:
1694-
raiseAssertionError(
1695-
"label_namer {!r} can't be found in the parameter names "
1696-
"(known argnames: %s).".format(label_namer,arg_names))
1697-
else:
1698-
# this is the case when the name is in arg_names
1699-
pass
1700-
1701-
@functools.wraps(func)
1702-
definner(ax,*args,**kwargs):
1703-
# this is needed because we want to change these values if
1704-
# arg_names_at_runtime==True, but python does not allow assigning
1705-
# to a variable in a outer scope. So use some new local ones and
1706-
# set them to the already computed values.
1707-
_label_pos=label_pos
1708-
_label_namer_pos=label_namer_pos
1709-
_arg_names=arg_names
1710-
1711-
label=None
1596+
arg_names.append(p.name)
1597+
data_param=Parameter("data",Parameter.KEYWORD_ONLY,default=None)
1598+
ifvarkwargs_name:
1599+
params.insert(-1,data_param)
1600+
else:
1601+
params.append(data_param)
1602+
new_sig=sig.replace(parameters=params)
1603+
arg_names=arg_names[1:]# remove the first "ax" / self arg
17121604

1713-
data=kwargs.pop('data',None)
1605+
ifreplace_namesisnotNone:
1606+
replace_names=set(replace_names)
17141607

1715-
ifdataisNone:# data validation
1716-
args=tuple(sanitize_sequence(a)forainargs)
1717-
else:
1718-
ifarg_names_at_runtime:
1719-
# update the information about replace names and
1720-
# label position
1721-
_arg_names=positional_parameter_names(args,data)
1722-
if (label_namerand# we actually want a label here ...
1723-
_arg_namesand# and we can find a label in *args
1724-
(label_namerin_arg_names)):# and it is in *args
1725-
_label_namer_pos=_arg_names.index(label_namer)
1726-
if"label"in_arg_names:
1727-
_label_pos=arg_names.index("label")
1728-
1729-
# save the current label_namer value so that it can be used as
1730-
# a label
1731-
if_label_namer_pos<len(args):
1732-
label=args[_label_namer_pos]
1733-
else:
1734-
label=kwargs.get(label_namer,None)
1735-
# ensure a string, as label can't be anything else
1736-
ifnotisinstance(label,str):
1737-
label=None
1738-
1739-
if (replace_namesisNone)or (replace_all_argsisTrue):
1740-
# all should be replaced
1741-
args=tuple(_replacer(data,a)for
1742-
j,ainenumerate(args))
1743-
else:
1744-
# An arg is replaced if the arg_name of that position is
1745-
# in replace_names ...
1746-
iflen(_arg_names)<len(args):
1747-
raiseRuntimeError(
1748-
"Got more args than function expects")
1749-
args=tuple(_replacer(data,a)
1750-
if_arg_names[j]inreplace_nameselsea
1751-
forj,ainenumerate(args))
1608+
assert (replace_namesorset())<=set(arg_names)orvarkwargs_name, (
1609+
"Matplotlib internal error: invalid replace_names ({!r}) for {!r}"
1610+
.format(replace_names,func.__name__))
1611+
assertlabel_namerisNoneorlabel_namerinarg_namesorvarkwargs_name, (
1612+
"Matplotlib internal error: invalid label_namer ({!r}) for {!r}"
1613+
.format(label_namer,func.__name__))
1614+
1615+
@functools.wraps(func)
1616+
definner(ax,*args,**kwargs):
1617+
data=kwargs.pop("data",None)
1618+
ifdataisNone:
1619+
returnfunc(ax,*args,**kwargs)
17521620

1621+
bound=new_sig.bind(ax,*args,**kwargs)
1622+
needs_label= (label_namer
1623+
and"label"notinbound.arguments
1624+
and"label"notinbound.kwargs)
1625+
auto_label= (bound.arguments.get(label_namer)
1626+
orbound.kwargs.get(label_namer))
1627+
ifnotisinstance(auto_label,str):
1628+
auto_label=None
1629+
1630+
fork,vinbound.arguments.items():
1631+
ifk==varkwargs_name:
1632+
fork1,v1inv.items():
1633+
ifreplace_namesisNoneork1inreplace_names:
1634+
v[k1]=_replacer(data,v1)
1635+
elifk==varargs_name:
17531636
ifreplace_namesisNone:
1754-
# replace all kwargs ...
1755-
kwargs= {k:_replacer(data,v)fork,vinkwargs.items()}
1756-
else:
1757-
# ... or only if a kwarg of that name is in replace_names
1758-
kwargs= {
1759-
k:_replacer(data,v)ifkinreplace_nameselsev
1760-
fork,vinkwargs.items()}
1761-
1762-
# replace the label if this func "wants" a label arg and the user
1763-
# didn't set one. Note: if the user puts in "label=None", it does
1764-
# *NOT* get replaced!
1765-
user_supplied_label= (
1766-
len(args)>=_label_posor# label is included in args
1767-
'label'inkwargs# ... or in kwargs
1768-
)
1769-
iflabel_namerandnotuser_supplied_label:
1770-
if_label_namer_pos<len(args):
1771-
kwargs['label']=get_label(args[_label_namer_pos],label)
1772-
eliflabel_namerinkwargs:
1773-
kwargs['label']=get_label(kwargs[label_namer],label)
1637+
bound.arguments[k]=tuple(
1638+
_replacer(data,v1)forv1inv)
1639+
else:
1640+
ifreplace_namesisNoneorkinreplace_names:
1641+
bound.arguments[k]=_replacer(data,v)
1642+
1643+
bound.apply_defaults()
1644+
delbound.arguments["data"]
1645+
1646+
all_kwargs= {**bound.arguments,**bound.kwargs}
1647+
ifneeds_label:
1648+
iflabel_namernotinall_kwargs:
1649+
warnings.warn(
1650+
"Tried to set a label via parameter %r in func %r but "
1651+
"couldn't find such an argument.\n"
1652+
"(This is a programming error, please report to "
1653+
"the Matplotlib list!)"% (label_namer,func.__name__),
1654+
RuntimeWarning,stacklevel=2)
1655+
else:
1656+
label=get_label(all_kwargs[label_namer],auto_label)
1657+
if"label"inarg_names:
1658+
bound.arguments["label"]=label
1659+
try:
1660+
bound.arguments.move_to_end(varkwargs_name)
1661+
exceptKeyError:
1662+
pass
17741663
else:
1775-
warnings.warn(
1776-
"Tried to set a label via parameter %r in func %r but "
1777-
"couldn't find such an argument.\n"
1778-
"(This is a programming error, please report to "
1779-
"the Matplotlib list!)"% (label_namer,func.__name__),
1780-
RuntimeWarning,stacklevel=2)
1781-
returnfunc(ax,*args,**kwargs)
1664+
bound.arguments.setdefault(
1665+
varkwargs_name, {})["label"]=label
17821666

1783-
inner.__doc__=_add_data_doc(inner.__doc__,
1784-
replace_names,replace_all_args)
1785-
inner.__signature__=new_sig
1786-
returninner
1667+
returnfunc(*bound.args,**bound.kwargs)
17871668

1788-
returnparam
1669+
inner.__doc__=_add_data_doc(inner.__doc__,replace_names)
1670+
inner.__signature__=new_sig
1671+
returninner
17891672

17901673
_log.debug('matplotlib version %s',__version__)
17911674
_log.debug('interactive is %s',is_interactive())

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp