diff --git a/.coveragerc b/.coveragerc index 9712b4f..6678b95 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,6 +1,6 @@ [run] source = - data_prototype + mpl_data_containers [report] omit = */python?.?/* diff --git a/.flake8 b/.flake8 index 2399875..f283183 100644 --- a/.flake8 +++ b/.flake8 @@ -5,7 +5,7 @@ exclude = build, dist, versioneer.py, - data_prototype/_version.py, + mpl_data_containers/_version.py, docs/source/conf.py setup.py .eggs diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 398e6bf..49a479d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -5,7 +5,7 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: "Set up Python 3.10" diff --git a/.gitignore b/.gitignore index 0c85e40..1162c72 100644 --- a/.gitignore +++ b/.gitignore @@ -103,4 +103,4 @@ doc/_static/constrained_layout*.png # setuptools_scm collateral -data_prototype/_version.py \ No newline at end of file +mpl_data_containers/_version.py diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 4f757ba..184baf9 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -13,7 +13,7 @@ Types of Contributions Report Bugs ~~~~~~~~~~~ -Report bugs at https://github.com/tacaswell/data_prototype/issues. +Report bugs at https://github.com/matplotlib/data-prototype/issues. If you are reporting a bug, please include: @@ -35,14 +35,14 @@ is open to whoever wants to implement it. Write Documentation ~~~~~~~~~~~~~~~~~~~ -data_prototype could always use more documentation, whether -as part of the official data_prototype docs, in docstrings, +mpl_data_containers could always use more documentation, whether +as part of the official mpl_data_containers docs, in docstrings, or even on the web in blog posts, articles, and such. Submit Feedback ~~~~~~~~~~~~~~~ -The best way to send feedback is to file an issue at https://github.com/tacaswell/data_prototype/issues. +The best way to send feedback is to file an issue at https://github.com/matplotlib/mpl_data_containers/issues. If you are proposing a feature: @@ -54,17 +54,17 @@ If you are proposing a feature: Get Started! ------------ -Ready to contribute? Here's how to set up `data_prototype` for local development. +Ready to contribute? Here's how to set up `mpl_data_containers` for local development. -1. Fork the `data_prototype` repo on GitHub. +1. Fork the `mpl_data_containers` repo on GitHub. 2. Clone your fork locally:: - $ git clone git@github.com:your_name_here/data_prototype.git + $ git clone git@github.com:your_name_here/mpl_data_containers.git 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: - $ mkvirtualenv data_prototype - $ cd data_prototype/ + $ mkvirtualenv mpl_data_containers + $ cd mpl_data_containers/ $ python setup.py develop 4. Create a branch for local development:: @@ -98,7 +98,5 @@ Before you submit a pull request, check that it meets these guidelines: 2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst. -3. The pull request should work for Python 2.7, 3.3, 3.4, 3.5 and for PyPy. Check - https://travis-ci.org/tacaswell/data_prototype/pull_requests - and make sure that the tests pass for all supported Python versions. +3. The pull request should work for all supported python versions. diff --git a/MANIFEST.in b/MANIFEST.in index 71fcfae..fcb8c6d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,7 +9,7 @@ recursive-exclude * *.py[co] recursive-include docs *.rst conf.py Makefile make.bat -include data_prototype/_version.py +include mpl_data_containers/_version.py # If including data files in the package, add them like: # include path/to/data_file diff --git a/README.rst b/README.rst index d9059ef..2f809de 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,6 @@ -============== -data_prototype -============== +=================== +mpl_data_containers +=================== Experimental code for the upcoming Matplotlib data refactor. diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst index aea4358..a1e3bfd 100644 --- a/docs/source/api/index.rst +++ b/docs/source/api/index.rst @@ -7,7 +7,7 @@ API Containers ========== -.. automodule:: data_prototype.containers +.. automodule:: mpl_data_containers.containers :members: :undoc-members: @@ -16,10 +16,10 @@ Containers Wrappers ======== -.. automodule:: data_prototype.wrappers +.. automodule:: mpl_data_containers.wrappers :members: :undoc-members: -.. automodule:: data_prototype.patches +.. automodule:: mpl_data_containers.patches :members: :undoc-members: diff --git a/docs/source/conf.py b/docs/source/conf.py index 5530ac9..bcdde7a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # -# data_prototype documentation build configuration file, created by +# mpl_data_containers documentation build configuration file, created by # sphinx-quickstart on Thu Jun 28 12:35:56 2018. # # This file is execfile()d with the current directory set to its @@ -23,7 +23,7 @@ # sys.path.insert(0, os.path.abspath('.')) from pathlib import Path -import data_prototype +import mpl_data_containers # are we running circle CI? CIRCLECI = "CIRCLECI" in os.environ @@ -72,7 +72,7 @@ ], "filename_pattern": "^((?!sgskip).)*$", "gallery_dirs": ["gallery"], - "doc_module": ("data_prototype",), + "doc_module": ("mpl_data_containers",), "reference_url": { "matplotlib": None, }, @@ -104,7 +104,7 @@ master_doc = "index" # General information about the project. -project = "data_prototype" +project = "mpl_data_containers" copyright = "2022, Thomas A Caswell" author = "Thomas A Caswell" @@ -114,9 +114,9 @@ # # The short X.Y version. -version = data_prototype.__version__ +version = mpl_data_containers.__version__ # The full version, including alpha/beta/rc tags. -release = data_prototype.__version__ +release = mpl_data_containers.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -179,7 +179,7 @@ # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. -htmlhelp_basename = "data_prototype" +htmlhelp_basename = "mpl_data_containers" # -- Options for LaTeX output --------------------------------------------- @@ -205,8 +205,8 @@ latex_documents = [ ( master_doc, - "data_prototype.tex", - "data_prototype Documentation", + "mpl_data_containers.tex", + "mpl_data_containers Documentation", "Contributors", "manual", ), @@ -218,7 +218,13 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, "data_prototype", "data_prototype Documentation", [author], 1) + ( + master_doc, + "mpl_data_containers", + "mpl_data_containers Documentation", + [author], + 1, + ) ] @@ -230,10 +236,10 @@ texinfo_documents = [ ( master_doc, - "data_prototype", - "data_prototype Documentation", + "mpl_data_containers", + "mpl_data_containers Documentation", author, - "data_prototype", + "mpl_data_containers", "Experimental code for the upcoming Matplotlib data refactor.", "Miscellaneous", ), diff --git a/docs/source/index.rst b/docs/source/index.rst index 1c91676..feef25a 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,8 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -data_prototype Documentation -============================ +mpl_data_containers Documentation +================================= This is prototype development for the next generation of data structures for Matplotlib. This is the version "to throw away". Everything in this diff --git a/docs/source/installation.rst b/docs/source/installation.rst index c52bee5..c33a471 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -4,4 +4,4 @@ Installation At the command line:: - $ pip install git+https://github.com/tacaswell/data-prototype.git@main + $ pip install git+https://github.com/matplotlib/data-prototype.git@main diff --git a/examples/2Dfunc.py b/examples/2Dfunc.py index 883b932..a033252 100644 --- a/examples/2Dfunc.py +++ b/examples/2Dfunc.py @@ -10,9 +10,9 @@ import matplotlib.pyplot as plt import numpy as np -from data_prototype.artist import CompatibilityAxes -from data_prototype.image import Image -from data_prototype.containers import FuncContainer +from mpl_data_containers.artist import CompatibilityAxes +from mpl_data_containers.image import Image +from mpl_data_containers.containers import FuncContainer from matplotlib.colors import Normalize diff --git a/examples/animation.py b/examples/animation.py index ee2cf45..74b5b82 100644 --- a/examples/animation.py +++ b/examples/animation.py @@ -17,14 +17,14 @@ import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation -from data_prototype.conversion_edge import Graph -from data_prototype.description import Desc +from mpl_data_containers.conversion_edge import Graph +from mpl_data_containers.description import Desc -from data_prototype.artist import CompatibilityAxes -from data_prototype.line import Line -from data_prototype.text import Text -from data_prototype.conversion_edge import FuncEdge +from mpl_data_containers.artist import CompatibilityAxes +from mpl_data_containers.line import Line +from mpl_data_containers.text import Text +from mpl_data_containers.conversion_edge import FuncEdge class SinOfTime: diff --git a/examples/data_frame.py b/examples/data_frame.py index 201a33f..f65617e 100644 --- a/examples/data_frame.py +++ b/examples/data_frame.py @@ -11,9 +11,9 @@ import numpy as np import pandas as pd -from data_prototype.artist import CompatibilityArtist as CA -from data_prototype.line import Line -from data_prototype.containers import DataFrameContainer +from mpl_data_containers.artist import CompatibilityArtist as CA +from mpl_data_containers.line import Line +from mpl_data_containers.containers import DataFrameContainer th = np.linspace(0, 4 * np.pi, 256) diff --git a/examples/errorbar.py b/examples/errorbar.py index fcf03ff..b4f1a4f 100644 --- a/examples/errorbar.py +++ b/examples/errorbar.py @@ -11,8 +11,8 @@ import numpy as np -from data_prototype.wrappers import ErrorbarWrapper -from data_prototype.containers import ArrayContainer +from mpl_data_containers.wrappers import ErrorbarWrapper +from mpl_data_containers.containers import ArrayContainer x = np.arange(10) y = x**2 diff --git a/examples/first.py b/examples/first.py index d8baf8d..3a9deb8 100644 --- a/examples/first.py +++ b/examples/first.py @@ -11,9 +11,9 @@ import numpy as np import pandas as pd -from data_prototype.artist import CompatibilityAxes -from data_prototype.line import Line -from data_prototype.containers import FuncContainer, SeriesContainer +from mpl_data_containers.artist import CompatibilityAxes +from mpl_data_containers.line import Line +from mpl_data_containers.containers import FuncContainer, SeriesContainer fc = FuncContainer({"x": (("N",), lambda x: x), "y": (("N",), lambda x: np.sin(1 / x))}) diff --git a/examples/hist.py b/examples/hist.py index 4fd45d3..7381f32 100644 --- a/examples/hist.py +++ b/examples/hist.py @@ -10,8 +10,8 @@ import matplotlib.pyplot as plt import numpy as np -from data_prototype.wrappers import StepWrapper -from data_prototype.containers import HistContainer +from mpl_data_containers.wrappers import StepWrapper +from mpl_data_containers.containers import HistContainer hc = HistContainer( np.concatenate([np.random.randn(5000), 0.1 * np.random.randn(500) + 5]), 25 diff --git a/examples/lissajous.py b/examples/lissajous.py index 777838e..2c58d99 100644 --- a/examples/lissajous.py +++ b/examples/lissajous.py @@ -19,10 +19,10 @@ import matplotlib.markers as mmarkers from matplotlib.animation import FuncAnimation -from data_prototype.conversion_edge import Graph -from data_prototype.description import Desc +from mpl_data_containers.conversion_edge import Graph +from mpl_data_containers.description import Desc -from data_prototype.wrappers import PathCollectionWrapper +from mpl_data_containers.wrappers import PathCollectionWrapper class Lissajous: diff --git a/examples/mandelbrot.py b/examples/mandelbrot.py index 9f447e2..eca2094 100644 --- a/examples/mandelbrot.py +++ b/examples/mandelbrot.py @@ -13,9 +13,9 @@ import matplotlib.pyplot as plt import numpy as np -from data_prototype.artist import CompatibilityAxes -from data_prototype.image import Image -from data_prototype.containers import FuncContainer +from mpl_data_containers.artist import CompatibilityAxes +from mpl_data_containers.image import Image +from mpl_data_containers.containers import FuncContainer from matplotlib.colors import Normalize diff --git a/examples/mapped.py b/examples/mapped.py index 4800019..90c0998 100644 --- a/examples/mapped.py +++ b/examples/mapped.py @@ -12,12 +12,12 @@ from matplotlib.colors import Normalize -from data_prototype.artist import CompatibilityAxes -from data_prototype.line import Line -from data_prototype.containers import ArrayContainer -from data_prototype.description import Desc -from data_prototype.conversion_edge import FuncEdge -from data_prototype.text import Text +from mpl_data_containers.artist import CompatibilityAxes +from mpl_data_containers.line import Line +from mpl_data_containers.containers import ArrayContainer +from mpl_data_containers.description import Desc +from mpl_data_containers.conversion_edge import FuncEdge +from mpl_data_containers.text import Text cmap = plt.colormaps["viridis"] diff --git a/examples/mulivariate_cmap.py b/examples/mulivariate_cmap.py index 8b33ca8..f5ec57d 100644 --- a/examples/mulivariate_cmap.py +++ b/examples/mulivariate_cmap.py @@ -11,11 +11,11 @@ import matplotlib.pyplot as plt import numpy as np -from data_prototype.image import Image -from data_prototype.artist import CompatibilityAxes -from data_prototype.description import Desc -from data_prototype.containers import FuncContainer -from data_prototype.conversion_edge import FuncEdge +from mpl_data_containers.image import Image +from mpl_data_containers.artist import CompatibilityAxes +from mpl_data_containers.description import Desc +from mpl_data_containers.containers import FuncContainer +from mpl_data_containers.conversion_edge import FuncEdge from matplotlib.colors import hsv_to_rgb diff --git a/examples/new_patch.py b/examples/new_patch.py index 2c9d4c3..124265f 100644 --- a/examples/new_patch.py +++ b/examples/new_patch.py @@ -12,9 +12,9 @@ import matplotlib.pyplot as plt -from data_prototype.artist import CompatibilityAxes -from data_prototype.patches import Patch -from data_prototype.containers import ArrayContainer +from mpl_data_containers.artist import CompatibilityAxes +from mpl_data_containers.patches import Patch +from mpl_data_containers.containers import ArrayContainer from matplotlib.path import Path diff --git a/examples/scatter_with_custom_axes.py b/examples/scatter_with_custom_axes.py index a5122d4..a781b2d 100644 --- a/examples/scatter_with_custom_axes.py +++ b/examples/scatter_with_custom_axes.py @@ -4,19 +4,19 @@ ========================================= This is a quick comparison between the current Matplotlib `scatter` and -the version in :file:`data_prototype/axes.py`, which uses data containers +the version in :file:`mpl_data_containers/axes.py`, which uses data containers and a conversion pipeline. This is here to show what does work and what does not work with the current implementation of container-based artist drawing. """ -import data_prototype.axes # side-effect registers projection # noqa +import mpl_data_containers.axes # side-effect registers projection # noqa import matplotlib.pyplot as plt fig = plt.figure() -newstyle = fig.add_subplot(2, 1, 1, projection="data-prototype") +newstyle = fig.add_subplot(2, 1, 1, projection="mpl-data-containers") oldstyle = fig.add_subplot(2, 1, 2) newstyle.scatter([0, 1, 2], [2, 5, 1]) diff --git a/examples/simple_patch.py b/examples/simple_patch.py index ebfe959..1c1febb 100644 --- a/examples/simple_patch.py +++ b/examples/simple_patch.py @@ -14,10 +14,10 @@ import matplotlib.pyplot as plt import matplotlib.patches as mpatches -from data_prototype.containers import ArrayContainer -from data_prototype.artist import CompatibilityAxes +from mpl_data_containers.containers import ArrayContainer +from mpl_data_containers.artist import CompatibilityAxes -from data_prototype.patches import Rectangle +from mpl_data_containers.patches import Rectangle cont1 = ArrayContainer( lower_left_x=np.array(-3), @@ -25,12 +25,10 @@ upper_right_x=np.array(-1), upper_right_y=np.array(3), edgecolor=np.array([0, 0, 0]), - hatch_color=np.array([0, 0, 0]), facecolor="green", linewidth=3, linestyle="-", antialiased=np.array([True]), - hatch="*", fill=np.array([True]), capstyle=np.array(["round"]), joinstyle=np.array(["miter"]), @@ -46,12 +44,10 @@ rotation_point_x=np.array(1), rotation_point_y=np.array(3.5), edgecolor=np.array([0.5, 0.2, 0]), - hatch_color=np.array([0, 0, 0]), facecolor="red", linewidth=6, linestyle="-", antialiased=np.array([True]), - hatch="", fill=np.array([True]), capstyle=np.array(["round"]), joinstyle=np.array(["miter"]), diff --git a/examples/simple_scatter.py b/examples/simple_scatter.py index 9b3fd90..ee21a30 100644 --- a/examples/simple_scatter.py +++ b/examples/simple_scatter.py @@ -12,9 +12,9 @@ import matplotlib.pyplot as plt import matplotlib.markers as mmarkers -from data_prototype.containers import ArrayContainer +from mpl_data_containers.containers import ArrayContainer -from data_prototype.wrappers import PathCollectionWrapper +from mpl_data_containers.wrappers import PathCollectionWrapper marker_obj = mmarkers.MarkerStyle("o") diff --git a/examples/subsample.py b/examples/subsample.py index 97a67d7..0357176 100644 --- a/examples/subsample.py +++ b/examples/subsample.py @@ -20,10 +20,10 @@ import numpy as np -from data_prototype.description import Desc, desc_like -from data_prototype.artist import CompatibilityArtist as CA -from data_prototype.image import Image -from data_prototype.containers import ArrayContainer +from mpl_data_containers.description import Desc, desc_like +from mpl_data_containers.artist import CompatibilityArtist as CA +from mpl_data_containers.image import Image +from mpl_data_containers.containers import ArrayContainer from skimage.transform import downscale_local_mean diff --git a/examples/units.py b/examples/units.py index 74a246f..ff4d2c6 100644 --- a/examples/units.py +++ b/examples/units.py @@ -12,12 +12,12 @@ import matplotlib.pyplot as plt import matplotlib.markers as mmarkers -from data_prototype.artist import CompatibilityAxes -from data_prototype.containers import ArrayContainer -from data_prototype.conversion_edge import FuncEdge -from data_prototype.description import Desc +from mpl_data_containers.artist import CompatibilityAxes +from mpl_data_containers.containers import ArrayContainer +from mpl_data_containers.conversion_edge import FuncEdge +from mpl_data_containers.description import Desc -from data_prototype.line import Line +from mpl_data_containers.line import Line import pint diff --git a/examples/widgets.py b/examples/widgets.py index ac19874..5043e12 100644 --- a/examples/widgets.py +++ b/examples/widgets.py @@ -14,11 +14,11 @@ import matplotlib.pyplot as plt from matplotlib.widgets import Slider, Button -from data_prototype.artist import CompatibilityArtist as CA -from data_prototype.line import Line -from data_prototype.containers import FuncContainer -from data_prototype.description import Desc -from data_prototype.conversion_edge import FuncEdge +from mpl_data_containers.artist import CompatibilityArtist as CA +from mpl_data_containers.line import Line +from mpl_data_containers.containers import FuncContainer +from mpl_data_containers.description import Desc +from mpl_data_containers.conversion_edge import FuncEdge class SliderContainer(FuncContainer): diff --git a/data_prototype/__init__.py b/mpl_data_containers/__init__.py similarity index 100% rename from data_prototype/__init__.py rename to mpl_data_containers/__init__.py diff --git a/data_prototype/artist.py b/mpl_data_containers/artist.py similarity index 100% rename from data_prototype/artist.py rename to mpl_data_containers/artist.py diff --git a/data_prototype/axes.py b/mpl_data_containers/axes.py similarity index 98% rename from data_prototype/axes.py rename to mpl_data_containers/axes.py index 5659701..68093c7 100644 --- a/data_prototype/axes.py +++ b/mpl_data_containers/axes.py @@ -19,7 +19,7 @@ class Axes(MPLAxes): # Name for registering as a projection so we can experiment with it - name = "data-prototype" + name = "mpl-data-containers" @_preprocess_data( replace_names=[ @@ -143,5 +143,5 @@ def scatter( return pcw -# This is a handy trick to allow e.g. plt.subplots(subplot_kw={'projection': 'data-prototype'}) +# This is a handy trick to allow e.g. plt.subplots(subplot_kw={'projection': 'mpl-data-containers'}) mprojections.register_projection(Axes) diff --git a/data_prototype/containers.py b/mpl_data_containers/containers.py similarity index 100% rename from data_prototype/containers.py rename to mpl_data_containers/containers.py diff --git a/data_prototype/conversion_edge.py b/mpl_data_containers/conversion_edge.py similarity index 89% rename from data_prototype/conversion_edge.py rename to mpl_data_containers/conversion_edge.py index 8f850b0..4c074d5 100644 --- a/data_prototype/conversion_edge.py +++ b/mpl_data_containers/conversion_edge.py @@ -7,7 +7,7 @@ from typing import Any import numpy as np -from data_prototype.description import Desc, desc_like, ShapeSpec +from mpl_data_containers.description import Desc, desc_like, ShapeSpec from matplotlib.transforms import Transform @@ -372,7 +372,20 @@ def edges(self): return SequenceEdge.from_edges("eval", out_edges, output) def visualize(self, input: dict[str, Desc] | None = None): - import networkx as nx + if input is None: + from .introspection import draw_graph + + draw_graph(self) + return + + try: + import networkx as nx + except ImportError: + from .introspection import draw_graph + + draw_graph(self) + return + import matplotlib.pyplot as plt from pprint import pformat @@ -382,38 +395,26 @@ def node_format(x): G = nx.DiGraph() - if input is not None: - for _, edges in self._subgraphs: - q: list[dict[str, Desc]] = [input] - explored: set[tuple[tuple[str, str], ...]] = set() - explored.add( - tuple(sorted(((k, v.coordinates) for k, v in q[0].items()))) - ) - G.add_node(node_format(q[0])) - while q: - n = q.pop() - for e in edges: - if Desc.compatible(n, e.input): - w = n | e.output - if node_format(w) not in G: - G.add_node(node_format(w)) - explored.add( - tuple( - sorted( - ((k, v.coordinates) for k, v in w.items()) - ) - ) + for _, edges in self._subgraphs: + q: list[dict[str, Desc]] = [input] + explored: set[tuple[tuple[str, str], ...]] = set() + explored.add(tuple(sorted(((k, v.coordinates) for k, v in q[0].items())))) + G.add_node(node_format(q[0])) + while q: + n = q.pop() + for e in edges: + if Desc.compatible(n, e.input): + w = n | e.output + if node_format(w) not in G: + G.add_node(node_format(w)) + explored.add( + tuple( + sorted(((k, v.coordinates) for k, v in w.items())) ) - q.append(w) - if node_format(w) != node_format(n): - G.add_edge(node_format(n), node_format(w), name=e.name) - else: - # don't bother separating subgraphs,as the end result is exactly the same here - for edge in self._edges: - G.add_edge( - node_format(edge.input), node_format(edge.output), name=edge.name - ) - + ) + q.append(w) + if node_format(w) != node_format(n): + G.add_edge(node_format(n), node_format(w), name=e.name) try: pos = nx.shell_layout(G) except Exception: diff --git a/data_prototype/conversion_node.py b/mpl_data_containers/conversion_node.py similarity index 100% rename from data_prototype/conversion_node.py rename to mpl_data_containers/conversion_node.py diff --git a/data_prototype/description.py b/mpl_data_containers/description.py similarity index 100% rename from data_prototype/description.py rename to mpl_data_containers/description.py diff --git a/data_prototype/image.py b/mpl_data_containers/image.py similarity index 100% rename from data_prototype/image.py rename to mpl_data_containers/image.py diff --git a/mpl_data_containers/introspection.py b/mpl_data_containers/introspection.py new file mode 100644 index 0000000..c9545ac --- /dev/null +++ b/mpl_data_containers/introspection.py @@ -0,0 +1,161 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +import graphlib +from pprint import pformat + +import matplotlib.pyplot as plt + +from .conversion_edge import Edge, Graph +from .description import Desc + + +@dataclass +class VisNode: + keys: list[str] + coordinates: list[str] + parents: list[VisNode] = field(default_factory=list) + children: list[VisNode] = field(default_factory=list) + x: int = 0 + y: int = 0 + + def __eq__(self, other): + return self.keys == other.keys and self.coordinates == other.coordinates + + def format(self): + return pformat({k: v for k, v in zip(self.keys, self.coordinates)}, width=20) + + +@dataclass +class VisEdge: + name: str + parent: VisNode + child: VisNode + + +def _position_subgraph( + subgraph: tuple(set[str], list[Edge]), +) -> tuple[list[VisNode], list[VisEdge]]: + # Build graph + nodes: list[VisNode] = [] + edges: list[VisEdge] = [] + + q: list[dict[str, Desc]] = [e.input for e in subgraph[1]] + explored: set[tuple[tuple[str, str], ...]] = set() + explored.add(tuple(sorted(((k, v.coordinates) for k, v in q[0].items())))) + + for e in subgraph[1]: + nodes.append( + VisNode(list(e.input.keys()), [x.coordinates for x in e.input.values()]) + ) + + while q: + n = q.pop() + vn = VisNode(list(n.keys()), [x.coordinates for x in n.values()]) + for nn in nodes: + if vn == nn: + vn = nn + + for e in subgraph[1]: + # Shortcut default edges appearing all over the place + if e.input == {} and vn.keys != []: + continue + if Desc.compatible(n, e.input): + w = e.output + vw = VisNode(list(w.keys()), [x.coordinates for x in w.values()]) + for nn in nodes: + if vw == nn: + vw = nn + + if vw not in nodes: + nodes.append(vw) + explored.add( + tuple(sorted(((k, v.coordinates) for k, v in w.items()))) + ) + q.append(w) + if vw != vn: + edges.append(VisEdge(e.name, vn, vw)) + vw.parents.append(vn) + vn.children.append(vw) + + # adapt graph for total ording + def hash_node(n): + return (tuple(n.keys), tuple(n.coordinates)) + + to_graph = {hash_node(n): set() for n in nodes} + for e in edges: + to_graph[hash_node(e.child)] |= {hash_node(e.parent)} + + # evaluate total ordering + topological_sorter = graphlib.TopologicalSorter(to_graph) + + # position horizontally by 1+ highest parent, vertically by 1+ highest sibling + def get_node(n): + for node in nodes: + if n[0] == tuple(node.keys) and n[1] == tuple(node.coordinates): + return node + + static_order = list(topological_sorter.static_order()) + + for n in static_order: + node = get_node(n) + if node.parents != []: + node.y = max(p.y for p in node.parents) + 1 + x_pos = {} + for n in static_order: + node = get_node(n) + if node.y in x_pos: + node.x = x_pos[node.y] + x_pos[node.y] += 1.25 + else: + x_pos[node.y] = 1.25 + + return nodes, edges + + +def draw_graph(graph: Graph, ax=None, *, adjust_axes=None): + if ax is None: + fig, ax = plt.subplots() + if adjust_axes is None: + adjust_axes = True + + inverted = adjust_axes or ax.yaxis.get_inverted() + + origin_y = 0 + xmax = 0 + + for sg in graph._subgraphs: + nodes, edges = _position_subgraph(sg) + annotations = {} + # Draw nodes + for node in nodes: + annotations[node.format()] = ax.annotate( + node.format(), + (node.x, node.y + origin_y), + ha="center", + va="center", + bbox={"boxstyle": "round", "facecolor": "none"}, + ) + + # Draw edges + for edge in edges: + arr = ax.annotate( + "", + (0.5, 1.05 if inverted else -0.05), + (0.5, -0.05 if inverted else 1.05), + xycoords=annotations[edge.child.format()], + textcoords=annotations[edge.parent.format()], + arrowprops={"arrowstyle": "->"}, + annotation_clip=True, + ) + ax.annotate(edge.name, (0.5, 0.5), xytext=(0.5, 0.5), textcoords=arr) + + origin_y += max(node.y for node in nodes) + 1 + xmax = max(xmax, max(node.x for node in nodes)) + + if adjust_axes: + ax.set_ylim(origin_y, -1) + ax.set_xlim(-1, xmax + 1) + ax.spines[:].set_visible(False) + ax.set_xticks([]) + ax.set_yticks([]) diff --git a/data_prototype/line.py b/mpl_data_containers/line.py similarity index 100% rename from data_prototype/line.py rename to mpl_data_containers/line.py diff --git a/data_prototype/patches.py b/mpl_data_containers/patches.py similarity index 98% rename from data_prototype/patches.py rename to mpl_data_containers/patches.py index d83262f..e14f225 100644 --- a/data_prototype/patches.py +++ b/mpl_data_containers/patches.py @@ -51,8 +51,8 @@ def draw(self, renderer, graph: Graph) -> None: "x": desc, "y": desc, "codes": desc, - "facecolor": Desc((), "display"), - "edgecolor": Desc(("M",), "display"), + "facecolor": scalar, + "edgecolor": scalar, "linewidth": scalar, "linestyle": scalar, "hatch": scalar, @@ -93,7 +93,7 @@ def draw(self, renderer, graph: Graph) -> None: if evald["hatch"] is not None: gc.set_hatch(evald["hatch"]) - gc.set_hatch_color(evald["hatch_color"]) + # gc.set_hatch_color(evald["hatch_color"]) # if self.get_sketch_params() is not None: # gc.set_sketch_params(*self.get_sketch_params()) diff --git a/data_prototype/tests/__init__.py b/mpl_data_containers/tests/__init__.py similarity index 100% rename from data_prototype/tests/__init__.py rename to mpl_data_containers/tests/__init__.py diff --git a/data_prototype/tests/conftest.py b/mpl_data_containers/tests/conftest.py similarity index 100% rename from data_prototype/tests/conftest.py rename to mpl_data_containers/tests/conftest.py diff --git a/data_prototype/tests/test_check_shape.py b/mpl_data_containers/tests/test_check_shape.py similarity index 98% rename from data_prototype/tests/test_check_shape.py rename to mpl_data_containers/tests/test_check_shape.py index f499761..47d8d0f 100644 --- a/data_prototype/tests/test_check_shape.py +++ b/mpl_data_containers/tests/test_check_shape.py @@ -1,6 +1,6 @@ import pytest -from data_prototype.description import Desc +from mpl_data_containers.description import Desc @pytest.mark.parametrize( diff --git a/data_prototype/tests/test_containers.py b/mpl_data_containers/tests/test_containers.py similarity index 100% rename from data_prototype/tests/test_containers.py rename to mpl_data_containers/tests/test_containers.py diff --git a/data_prototype/tests/test_examples.py b/mpl_data_containers/tests/test_examples.py similarity index 100% rename from data_prototype/tests/test_examples.py rename to mpl_data_containers/tests/test_examples.py diff --git a/data_prototype/text.py b/mpl_data_containers/text.py similarity index 100% rename from data_prototype/text.py rename to mpl_data_containers/text.py diff --git a/data_prototype/wrappers.py b/mpl_data_containers/wrappers.py similarity index 98% rename from data_prototype/wrappers.py rename to mpl_data_containers/wrappers.py index f80eb9b..de66c46 100644 --- a/data_prototype/wrappers.py +++ b/mpl_data_containers/wrappers.py @@ -18,10 +18,10 @@ ) from matplotlib.artist import Artist as _Artist -from data_prototype.containers import DataContainer, _MatplotlibTransform -from data_prototype.description import Desc, desc_like -from data_prototype.conversion_edge import TransformEdge, Graph -from data_prototype.conversion_node import ( +from mpl_data_containers.containers import DataContainer, _MatplotlibTransform +from mpl_data_containers.description import Desc, desc_like +from mpl_data_containers.conversion_edge import TransformEdge, Graph +from mpl_data_containers.conversion_node import ( ConversionNode, RenameConversionNode, evaluate_pipeline, diff --git a/setup.py b/setup.py index eb88ad7..814e73c 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ min_version = (3, 10) if sys.version_info < min_version: error = """ -data_prototype does not support Python {0}.{1}. +mpl_data_containers does not support Python {0}.{1}. Python {2}.{3} and above is required. Check your Python version like so: python3 --version @@ -38,12 +38,12 @@ setup( - name="data_prototype", + name="mpl_data_containers", description="Experimental code for the upcoming Matplotlib data refactor.", long_description=readme, - author="Thomas A Caswell", - author_email="tcaswell@gmail.com", - url="https://github.com/tacaswell/data_prototype", + author="Kyle Sunden", + author_email="pypi@ksunden.space", + url="https://github.com/matplotlib/data-prototype", python_requires=">={}".format(".".join(str(n) for n in min_version)), packages=find_packages(exclude=["docs", "tests"]), entry_points={ @@ -59,12 +59,12 @@ use_scm_version={ "version_scheme": "release-branch-semver", "local_scheme": "node-and-date", - "write_to": "data_prototype/_version.py", + "write_to": "mpl_data_containers/_version.py", "fallback_version": "0.0+UNKNOWN", }, include_package_data=True, package_data={ - "data_prototype": [ + "mpl_data_containers": [ # When adding files here, remember to update MANIFEST.in as well, # or else they will not be included in the distribution on PyPI! # 'path/to/data_file',