Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "properties" to all matplotlib artists #168

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
59 changes: 34 additions & 25 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,23 @@
API reference
=============

The comprehensive API reference. All of the below objects are imported
into the top-level namespace. Use ``help(pplt.object)`` to read
the docs during a python session.
Comprehensive documentation of ProPlot functions and classes. All of these
objects are imported into the top-level namespace, so you can read the
documentation within python sessions using ``help(pplt.function_or_class)``.
Please note that the "wrapper" function documentation from proplot < 0.8
is now located on the individual plotting commands under
`proplot.axes.PlotAxes`. When calling ``help(axes.command)`` on
plotting commands during a python session, both the ProPlot
documentation and the original matplotlib documentation are shown.

Top-level functions
===================

.. automodule:: proplot.ui

.. automodsumm:: proplot.ui
:toctree: api

Please note that the documentation for "wrapper" functions from
proplot < 0.8 is now found under the individual `~proplot.axes.PlotAxes`
plotting commands. Using ``help(ax.command)`` during a python session shows both
the proplot documentation and the original matplotlib documentation.

Figure class
============
Expand All @@ -20,16 +29,16 @@ Figure class

.. automodsumm:: proplot.figure
:toctree: api
:skip: SubplotsContainer


Grid classes
============
Gridspec class
==============

.. automodule:: proplot.gridspec

.. automodsumm:: proplot.gridspec
:toctree: api
:skip: SubplotsContainer


Axes classes
Expand All @@ -41,15 +50,6 @@ Axes classes
:toctree: api


Top-level functions
===================

.. automodule:: proplot.ui

.. automodsumm:: proplot.ui
:toctree: api


Configuration tools
===================

Expand Down Expand Up @@ -107,20 +107,29 @@ Projection classes
:toctree: api


Demo functions
==============
Artist subclass
===============

.. automodule:: proplot.demos
.. automodule:: proplot.artist

.. automodsumm:: proplot.demos
.. automodsumm:: proplot.artist
:toctree: api


Miscellaneous functions
=======================
Miscellaneous tools
===================

.. automodule:: proplot.utils

.. automodsumm:: proplot.utils
:toctree: api
:skip: shade, saturate


Demo functions
==============

.. automodule:: proplot.demos

.. automodsumm:: proplot.demos
:toctree: api
1 change: 1 addition & 0 deletions proplot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from .ui import * # noqa: F401 F403
with _benchmark('demos'):
from .demos import * # noqa: F401 F403
from .artist import * # noqa: F401 F403

# Dynamically add registered classes to top-level namespace
from .constructor import NORMS, LOCATORS, FORMATTERS, SCALES, PROJS
Expand Down
86 changes: 86 additions & 0 deletions proplot/artist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env python3
"""
Add dot-notation properties for matplotlib setter and getter functions.
"""
import warnings

import matplotlib.artist as martist
from matplotlib import MatplotlibDeprecationWarning

__all__ = []


PROPS_IGNORE = (
# Axes props
'axes',
'figure',
'xaxis', # axes prop
'yaxis', # axes prop
'zaxis', # axes prop
'units', # axis prop
'gridlines',
# Method conflicts
'legend',
'tight_layout',
# Class-level conflicts
'contains',
'zorder',
'pickradius', # line property related to 'contains'
# Instance-level artist conflicts
'label',
'label_position',
# Instance-level axes conflicts
'cmap',
'norm',
'lines',
'images',
'title', # TODO: use internal title handling
)


def _iter_subclasses(cls):
"""
Iterate through all subclasses.
"""
yield cls
try:
for subclass in cls.__subclasses__():
yield from _iter_subclasses(subclass)
except TypeError:
pass


def _add_properties(cls):
"""
Generate property definitions for every artist getter.
"""
for attr in dir(cls):
try:
getter = getattr(cls, attr)
except MatplotlibDeprecationWarning:
continue
if not callable(getter) or attr[:4] != 'get_':
continue
prop = attr[4:]
if prop in PROPS_IGNORE:
continue
if hasattr(cls, prop):
value = getattr(cls, prop)
if not isinstance(value, property): # i.e. this is not child of a class
warnings._warn_proplot(f'Skipping property {prop!r}. Already exists as attribute.') # noqa: E501
continue
args = [getter] # property() function args
setter = getattr(cls, 'set_' + prop, None)
if callable(setter):
args.append(setter)
obj = property(*args, doc=getter.__doc__)
setattr(cls, prop, obj)


# Apply properties
# NOTE: While we can guard against class-level attribute conflicts we *cannot* guard
# against instance-level attribute conflicts. Therefore this may never work.
for cls in _iter_subclasses(martist.Artist):
with warnings.catch_warnings():
warnings.simplefilter('error', MatplotlibDeprecationWarning)
_add_properties(cls)