Skip to content

Commit 659b748

Browse files
committed
Avoid class-level conflicts, begin to find instance-level conflicts
1 parent 6200379 commit 659b748

File tree

1 file changed

+66
-30
lines changed

1 file changed

+66
-30
lines changed

proplot/artist.py

+66-30
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,86 @@
11
#!/usr/bin/env python3
22
"""
3-
Create a metaclass
3+
Add dot-notation properties for matplotlib setter and getter functions.
44
"""
5+
import warnings
6+
57
import matplotlib.artist as martist
8+
from matplotlib import MatplotlibDeprecationWarning
69

710
__all__ = []
811

9-
PROPS_PROTECTED = [
10-
'figure', 'axes', 'pickradius',
11-
]
12+
13+
PROPS_IGNORE = (
14+
# Axes props
15+
'axes',
16+
'figure',
17+
'xaxis', # axes prop
18+
'yaxis', # axes prop
19+
'zaxis', # axes prop
20+
'units', # axis prop
21+
'gridlines',
22+
# Method conflicts
23+
'legend',
24+
'tight_layout',
25+
# Class-level conflicts
26+
'contains',
27+
'zorder',
28+
'pickradius', # line property related to 'contains'
29+
# Instance-level artist conflicts
30+
'label',
31+
'label_position',
32+
# Instance-level axes conflicts
33+
'cmap',
34+
'norm',
35+
'lines',
36+
'images',
37+
'title', # TODO: use internal title handling
38+
)
1239

1340

14-
def _gen_subclasses(cls):
41+
def _iter_subclasses(cls):
1542
"""
16-
Generate all of a class's sublcasses with recursion.
43+
Iterate through all subclasses.
1744
"""
45+
yield cls
1846
try:
1947
for subclass in cls.__subclasses__():
20-
yield subclass
21-
for subclass in _gen_subclasses(subclass):
22-
yield subclass
48+
yield from _iter_subclasses(subclass)
2349
except TypeError:
24-
return
50+
pass
2551

2652

27-
def _gen_properties(cls):
53+
def _add_properties(cls):
2854
"""
2955
Generate property definitions for every artist getter.
3056
"""
3157
for attr in dir(cls):
32-
obj = getattr(cls, attr)
33-
if callable(obj) and attr[:4] == 'get_':
34-
getter = obj
35-
name = attr[4:]
36-
if hasattr(cls, name):
37-
continue
38-
elif name in PROPS_PROTECTED:
39-
continue
40-
args = [getter] # property args
41-
setter = getattr(cls, 'set_' + name, None)
42-
if callable(setter):
43-
args.append(setter)
44-
yield name, property(*args, doc=getter.__doc__)
45-
46-
47-
for cls in _gen_subclasses(martist.Artist):
48-
for name, prop in _gen_properties(cls):
49-
setattr(cls, name, prop)
50-
print(f'Added properties to {cls.__name__}.')
58+
try:
59+
getter = getattr(cls, attr)
60+
except MatplotlibDeprecationWarning:
61+
continue
62+
if not callable(getter) or attr[:4] != 'get_':
63+
continue
64+
prop = attr[4:]
65+
if prop in PROPS_IGNORE:
66+
continue
67+
if hasattr(cls, prop):
68+
value = getattr(cls, prop)
69+
if not isinstance(value, property): # i.e. this is not child of a class
70+
warnings._warn_proplot(f'Skipping property {prop!r}. Already exists as attribute.') # noqa: E501
71+
continue
72+
args = [getter] # property() function args
73+
setter = getattr(cls, 'set_' + prop, None)
74+
if callable(setter):
75+
args.append(setter)
76+
obj = property(*args, doc=getter.__doc__)
77+
setattr(cls, prop, obj)
78+
79+
80+
# Apply properties
81+
# NOTE: While we can guard against class-level attribute conflicts we *cannot* guard
82+
# against instance-level attribute conflicts. Therefore this may never work.
83+
for cls in _iter_subclasses(martist.Artist):
84+
with warnings.catch_warnings():
85+
warnings.simplefilter('error', MatplotlibDeprecationWarning)
86+
_add_properties(cls)

0 commit comments

Comments
 (0)