aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/cycler
diff options
context:
space:
mode:
authorshumkovnd <shumkovnd@yandex-team.com>2023-11-10 14:39:34 +0300
committershumkovnd <shumkovnd@yandex-team.com>2023-11-10 16:42:24 +0300
commit77eb2d3fdcec5c978c64e025ced2764c57c00285 (patch)
treec51edb0748ca8d4a08d7c7323312c27ba1a8b79a /contrib/python/cycler
parentdd6d20cadb65582270ac23f4b3b14ae189704b9d (diff)
downloadydb-77eb2d3fdcec5c978c64e025ced2764c57c00285.tar.gz
KIKIMR-19287: add task_stats_drawing script
Diffstat (limited to 'contrib/python/cycler')
-rw-r--r--contrib/python/cycler/py2/.dist-info/METADATA25
-rw-r--r--contrib/python/cycler/py2/.dist-info/top_level.txt1
-rw-r--r--contrib/python/cycler/py2/LICENSE27
-rw-r--r--contrib/python/cycler/py2/README.rst4
-rw-r--r--contrib/python/cycler/py2/cycler.py560
-rw-r--r--contrib/python/cycler/py2/ya.make26
-rw-r--r--contrib/python/cycler/py3/.dist-info/METADATA78
-rw-r--r--contrib/python/cycler/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/cycler/py3/LICENSE27
-rw-r--r--contrib/python/cycler/py3/README.rst21
-rw-r--r--contrib/python/cycler/py3/cycler/__init__.py573
-rw-r--r--contrib/python/cycler/py3/cycler/py.typed0
-rw-r--r--contrib/python/cycler/py3/ya.make23
-rw-r--r--contrib/python/cycler/ya.make18
14 files changed, 1384 insertions, 0 deletions
diff --git a/contrib/python/cycler/py2/.dist-info/METADATA b/contrib/python/cycler/py2/.dist-info/METADATA
new file mode 100644
index 0000000000..b232cee593
--- /dev/null
+++ b/contrib/python/cycler/py2/.dist-info/METADATA
@@ -0,0 +1,25 @@
+Metadata-Version: 2.0
+Name: cycler
+Version: 0.10.0
+Summary: Composable style cycles
+Home-page: http://github.com/matplotlib/cycler
+Author: Thomas A Caswell
+Author-email: matplotlib-users@python.org
+License: BSD
+Keywords: cycle kwargs
+Platform: Cross platform (Linux
+Platform: Mac OSX
+Platform: Windows)
+Classifier: Development Status :: 4 - Beta
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Requires-Dist: six
+
+UNKNOWN
+
+
diff --git a/contrib/python/cycler/py2/.dist-info/top_level.txt b/contrib/python/cycler/py2/.dist-info/top_level.txt
new file mode 100644
index 0000000000..22546440fa
--- /dev/null
+++ b/contrib/python/cycler/py2/.dist-info/top_level.txt
@@ -0,0 +1 @@
+cycler
diff --git a/contrib/python/cycler/py2/LICENSE b/contrib/python/cycler/py2/LICENSE
new file mode 100644
index 0000000000..d41d808995
--- /dev/null
+++ b/contrib/python/cycler/py2/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2015, matplotlib project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the matplotlib project nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/contrib/python/cycler/py2/README.rst b/contrib/python/cycler/py2/README.rst
new file mode 100644
index 0000000000..2fc73236ee
--- /dev/null
+++ b/contrib/python/cycler/py2/README.rst
@@ -0,0 +1,4 @@
+cycler: composable cycles
+=========================
+
+Docs: http://matplotlib.org/cycler/
diff --git a/contrib/python/cycler/py2/cycler.py b/contrib/python/cycler/py2/cycler.py
new file mode 100644
index 0000000000..8400444b2f
--- /dev/null
+++ b/contrib/python/cycler/py2/cycler.py
@@ -0,0 +1,560 @@
+"""
+Cycler
+======
+
+Cycling through combinations of values, producing dictionaries.
+
+You can add cyclers::
+
+ from cycler import cycler
+ cc = (cycler(color=list('rgb')) +
+ cycler(linestyle=['-', '--', '-.']))
+ for d in cc:
+ print(d)
+
+Results in::
+
+ {'color': 'r', 'linestyle': '-'}
+ {'color': 'g', 'linestyle': '--'}
+ {'color': 'b', 'linestyle': '-.'}
+
+
+You can multiply cyclers::
+
+ from cycler import cycler
+ cc = (cycler(color=list('rgb')) *
+ cycler(linestyle=['-', '--', '-.']))
+ for d in cc:
+ print(d)
+
+Results in::
+
+ {'color': 'r', 'linestyle': '-'}
+ {'color': 'r', 'linestyle': '--'}
+ {'color': 'r', 'linestyle': '-.'}
+ {'color': 'g', 'linestyle': '-'}
+ {'color': 'g', 'linestyle': '--'}
+ {'color': 'g', 'linestyle': '-.'}
+ {'color': 'b', 'linestyle': '-'}
+ {'color': 'b', 'linestyle': '--'}
+ {'color': 'b', 'linestyle': '-.'}
+"""
+
+from __future__ import (absolute_import, division, print_function,
+ unicode_literals)
+
+import six
+from itertools import product, cycle
+from six.moves import zip, reduce
+from operator import mul, add
+import copy
+
+__version__ = '0.10.0'
+
+
+def _process_keys(left, right):
+ """
+ Helper function to compose cycler keys
+
+ Parameters
+ ----------
+ left, right : iterable of dictionaries or None
+ The cyclers to be composed
+ Returns
+ -------
+ keys : set
+ The keys in the composition of the two cyclers
+ """
+ l_peek = next(iter(left)) if left is not None else {}
+ r_peek = next(iter(right)) if right is not None else {}
+ l_key = set(l_peek.keys())
+ r_key = set(r_peek.keys())
+ if l_key & r_key:
+ raise ValueError("Can not compose overlapping cycles")
+ return l_key | r_key
+
+
+class Cycler(object):
+ """
+ Composable cycles
+
+ This class has compositions methods:
+
+ ``+``
+ for 'inner' products (zip)
+
+ ``+=``
+ in-place ``+``
+
+ ``*``
+ for outer products (`itertools.product`) and integer multiplication
+
+ ``*=``
+ in-place ``*``
+
+ and supports basic slicing via ``[]``
+
+ Parameters
+ ----------
+ left : Cycler or None
+ The 'left' cycler
+
+ right : Cycler or None
+ The 'right' cycler
+
+ op : func or None
+ Function which composes the 'left' and 'right' cyclers.
+
+ """
+ def __call__(self):
+ return cycle(self)
+
+ def __init__(self, left, right=None, op=None):
+ """Semi-private init
+
+ Do not use this directly, use `cycler` function instead.
+ """
+ if isinstance(left, Cycler):
+ self._left = Cycler(left._left, left._right, left._op)
+ elif left is not None:
+ # Need to copy the dictionary or else that will be a residual
+ # mutable that could lead to strange errors
+ self._left = [copy.copy(v) for v in left]
+ else:
+ self._left = None
+
+ if isinstance(right, Cycler):
+ self._right = Cycler(right._left, right._right, right._op)
+ elif right is not None:
+ # Need to copy the dictionary or else that will be a residual
+ # mutable that could lead to strange errors
+ self._right = [copy.copy(v) for v in right]
+ else:
+ self._right = None
+
+ self._keys = _process_keys(self._left, self._right)
+ self._op = op
+
+ def __contains__(self, k):
+ return k in self._keys
+
+ @property
+ def keys(self):
+ """
+ The keys this Cycler knows about
+ """
+ return set(self._keys)
+
+ def change_key(self, old, new):
+ """
+ Change a key in this cycler to a new name.
+ Modification is performed in-place.
+
+ Does nothing if the old key is the same as the new key.
+ Raises a ValueError if the new key is already a key.
+ Raises a KeyError if the old key isn't a key.
+
+ """
+ if old == new:
+ return
+ if new in self._keys:
+ raise ValueError("Can't replace %s with %s, %s is already a key" %
+ (old, new, new))
+ if old not in self._keys:
+ raise KeyError("Can't replace %s with %s, %s is not a key" %
+ (old, new, old))
+
+ self._keys.remove(old)
+ self._keys.add(new)
+
+ if self._right is not None and old in self._right.keys:
+ self._right.change_key(old, new)
+
+ # self._left should always be non-None
+ # if self._keys is non-empty.
+ elif isinstance(self._left, Cycler):
+ self._left.change_key(old, new)
+ else:
+ # It should be completely safe at this point to
+ # assume that the old key can be found in each
+ # iteration.
+ self._left = [{new: entry[old]} for entry in self._left]
+
+ def _compose(self):
+ """
+ Compose the 'left' and 'right' components of this cycle
+ with the proper operation (zip or product as of now)
+ """
+ for a, b in self._op(self._left, self._right):
+ out = dict()
+ out.update(a)
+ out.update(b)
+ yield out
+
+ @classmethod
+ def _from_iter(cls, label, itr):
+ """
+ Class method to create 'base' Cycler objects
+ that do not have a 'right' or 'op' and for which
+ the 'left' object is not another Cycler.
+
+ Parameters
+ ----------
+ label : str
+ The property key.
+
+ itr : iterable
+ Finite length iterable of the property values.
+
+ Returns
+ -------
+ cycler : Cycler
+ New 'base' `Cycler`
+ """
+ ret = cls(None)
+ ret._left = list({label: v} for v in itr)
+ ret._keys = set([label])
+ return ret
+
+ def __getitem__(self, key):
+ # TODO : maybe add numpy style fancy slicing
+ if isinstance(key, slice):
+ trans = self.by_key()
+ return reduce(add, (_cycler(k, v[key])
+ for k, v in six.iteritems(trans)))
+ else:
+ raise ValueError("Can only use slices with Cycler.__getitem__")
+
+ def __iter__(self):
+ if self._right is None:
+ return iter(dict(l) for l in self._left)
+
+ return self._compose()
+
+ def __add__(self, other):
+ """
+ Pair-wise combine two equal length cycles (zip)
+
+ Parameters
+ ----------
+ other : Cycler
+ The second Cycler
+ """
+ if len(self) != len(other):
+ raise ValueError("Can only add equal length cycles, "
+ "not {0} and {1}".format(len(self), len(other)))
+ return Cycler(self, other, zip)
+
+ def __mul__(self, other):
+ """
+ Outer product of two cycles (`itertools.product`) or integer
+ multiplication.
+
+ Parameters
+ ----------
+ other : Cycler or int
+ The second Cycler or integer
+ """
+ if isinstance(other, Cycler):
+ return Cycler(self, other, product)
+ elif isinstance(other, int):
+ trans = self.by_key()
+ return reduce(add, (_cycler(k, v*other)
+ for k, v in six.iteritems(trans)))
+ else:
+ return NotImplemented
+
+ def __rmul__(self, other):
+ return self * other
+
+ def __len__(self):
+ op_dict = {zip: min, product: mul}
+ if self._right is None:
+ return len(self._left)
+ l_len = len(self._left)
+ r_len = len(self._right)
+ return op_dict[self._op](l_len, r_len)
+
+ def __iadd__(self, other):
+ """
+ In-place pair-wise combine two equal length cycles (zip)
+
+ Parameters
+ ----------
+ other : Cycler
+ The second Cycler
+ """
+ if not isinstance(other, Cycler):
+ raise TypeError("Cannot += with a non-Cycler object")
+ # True shallow copy of self is fine since this is in-place
+ old_self = copy.copy(self)
+ self._keys = _process_keys(old_self, other)
+ self._left = old_self
+ self._op = zip
+ self._right = Cycler(other._left, other._right, other._op)
+ return self
+
+ def __imul__(self, other):
+ """
+ In-place outer product of two cycles (`itertools.product`)
+
+ Parameters
+ ----------
+ other : Cycler
+ The second Cycler
+ """
+ if not isinstance(other, Cycler):
+ raise TypeError("Cannot *= with a non-Cycler object")
+ # True shallow copy of self is fine since this is in-place
+ old_self = copy.copy(self)
+ self._keys = _process_keys(old_self, other)
+ self._left = old_self
+ self._op = product
+ self._right = Cycler(other._left, other._right, other._op)
+ return self
+
+ def __eq__(self, other):
+ """
+ Check equality
+ """
+ if len(self) != len(other):
+ return False
+ if self.keys ^ other.keys:
+ return False
+
+ return all(a == b for a, b in zip(self, other))
+
+ def __repr__(self):
+ op_map = {zip: '+', product: '*'}
+ if self._right is None:
+ lab = self.keys.pop()
+ itr = list(v[lab] for v in self)
+ return "cycler({lab!r}, {itr!r})".format(lab=lab, itr=itr)
+ else:
+ op = op_map.get(self._op, '?')
+ msg = "({left!r} {op} {right!r})"
+ return msg.format(left=self._left, op=op, right=self._right)
+
+ def _repr_html_(self):
+ # an table showing the value of each key through a full cycle
+ output = "<table>"
+ sorted_keys = sorted(self.keys, key=repr)
+ for key in sorted_keys:
+ output += "<th>{key!r}</th>".format(key=key)
+ for d in iter(self):
+ output += "<tr>"
+ for k in sorted_keys:
+ output += "<td>{val!r}</td>".format(val=d[k])
+ output += "</tr>"
+ output += "</table>"
+ return output
+
+ def by_key(self):
+ """Values by key
+
+ This returns the transposed values of the cycler. Iterating
+ over a `Cycler` yields dicts with a single value for each key,
+ this method returns a `dict` of `list` which are the values
+ for the given key.
+
+ The returned value can be used to create an equivalent `Cycler`
+ using only `+`.
+
+ Returns
+ -------
+ transpose : dict
+ dict of lists of the values for each key.
+ """
+
+ # TODO : sort out if this is a bottle neck, if there is a better way
+ # and if we care.
+
+ keys = self.keys
+ out = {k: list() for k in keys}
+
+ for d in self:
+ for k in keys:
+ out[k].append(d[k])
+ return out
+
+ # for back compatibility
+ _transpose = by_key
+
+ def simplify(self):
+ """Simplify the Cycler
+
+ Returned as a composition using only sums (no multiplications)
+
+ Returns
+ -------
+ simple : Cycler
+ An equivalent cycler using only summation"""
+ # TODO: sort out if it is worth the effort to make sure this is
+ # balanced. Currently it is is
+ # (((a + b) + c) + d) vs
+ # ((a + b) + (c + d))
+ # I would believe that there is some performance implications
+
+ trans = self.by_key()
+ return reduce(add, (_cycler(k, v) for k, v in six.iteritems(trans)))
+
+ def concat(self, other):
+ """Concatenate this cycler and an other.
+
+ The keys must match exactly.
+
+ This returns a single Cycler which is equivalent to
+ `itertools.chain(self, other)`
+
+ Examples
+ --------
+
+ >>> num = cycler('a', range(3))
+ >>> let = cycler('a', 'abc')
+ >>> num.concat(let)
+ cycler('a', [0, 1, 2, 'a', 'b', 'c'])
+
+ Parameters
+ ----------
+ other : `Cycler`
+ The `Cycler` to concatenate to this one.
+
+ Returns
+ -------
+ ret : `Cycler`
+ The concatenated `Cycler`
+ """
+ return concat(self, other)
+
+
+def concat(left, right):
+ """Concatenate two cyclers.
+
+ The keys must match exactly.
+
+ This returns a single Cycler which is equivalent to
+ `itertools.chain(left, right)`
+
+ Examples
+ --------
+
+ >>> num = cycler('a', range(3))
+ >>> let = cycler('a', 'abc')
+ >>> num.concat(let)
+ cycler('a', [0, 1, 2, 'a', 'b', 'c'])
+
+ Parameters
+ ----------
+ left, right : `Cycler`
+ The two `Cycler` instances to concatenate
+
+ Returns
+ -------
+ ret : `Cycler`
+ The concatenated `Cycler`
+ """
+ if left.keys != right.keys:
+ msg = '\n\t'.join(["Keys do not match:",
+ "Intersection: {both!r}",
+ "Disjoint: {just_one!r}"]).format(
+ both=left.keys & right.keys,
+ just_one=left.keys ^ right.keys)
+
+ raise ValueError(msg)
+
+ _l = left.by_key()
+ _r = right.by_key()
+ return reduce(add, (_cycler(k, _l[k] + _r[k]) for k in left.keys))
+
+
+def cycler(*args, **kwargs):
+ """
+ Create a new `Cycler` object from a single positional argument,
+ a pair of positional arguments, or the combination of keyword arguments.
+
+ cycler(arg)
+ cycler(label1=itr1[, label2=iter2[, ...]])
+ cycler(label, itr)
+
+ Form 1 simply copies a given `Cycler` object.
+
+ Form 2 composes a `Cycler` as an inner product of the
+ pairs of keyword arguments. In other words, all of the
+ iterables are cycled simultaneously, as if through zip().
+
+ Form 3 creates a `Cycler` from a label and an iterable.
+ This is useful for when the label cannot be a keyword argument
+ (e.g., an integer or a name that has a space in it).
+
+ Parameters
+ ----------
+ arg : Cycler
+ Copy constructor for Cycler (does a shallow copy of iterables).
+
+ label : name
+ The property key. In the 2-arg form of the function,
+ the label can be any hashable object. In the keyword argument
+ form of the function, it must be a valid python identifier.
+
+ itr : iterable
+ Finite length iterable of the property values.
+ Can be a single-property `Cycler` that would
+ be like a key change, but as a shallow copy.
+
+ Returns
+ -------
+ cycler : Cycler
+ New `Cycler` for the given property
+
+ """
+ if args and kwargs:
+ raise TypeError("cyl() can only accept positional OR keyword "
+ "arguments -- not both.")
+
+ if len(args) == 1:
+ if not isinstance(args[0], Cycler):
+ raise TypeError("If only one positional argument given, it must "
+ " be a Cycler instance.")
+ return Cycler(args[0])
+ elif len(args) == 2:
+ return _cycler(*args)
+ elif len(args) > 2:
+ raise TypeError("Only a single Cycler can be accepted as the lone "
+ "positional argument. Use keyword arguments instead.")
+
+ if kwargs:
+ return reduce(add, (_cycler(k, v) for k, v in six.iteritems(kwargs)))
+
+ raise TypeError("Must have at least a positional OR keyword arguments")
+
+
+def _cycler(label, itr):
+ """
+ Create a new `Cycler` object from a property name and
+ iterable of values.
+
+ Parameters
+ ----------
+ label : hashable
+ The property key.
+
+ itr : iterable
+ Finite length iterable of the property values.
+
+ Returns
+ -------
+ cycler : Cycler
+ New `Cycler` for the given property
+ """
+ if isinstance(itr, Cycler):
+ keys = itr.keys
+ if len(keys) != 1:
+ msg = "Can not create Cycler from a multi-property Cycler"
+ raise ValueError(msg)
+
+ lab = keys.pop()
+ # Doesn't need to be a new list because
+ # _from_iter() will be creating that new list anyway.
+ itr = (v[lab] for v in itr)
+
+ return Cycler._from_iter(label, itr)
diff --git a/contrib/python/cycler/py2/ya.make b/contrib/python/cycler/py2/ya.make
new file mode 100644
index 0000000000..e139f85766
--- /dev/null
+++ b/contrib/python/cycler/py2/ya.make
@@ -0,0 +1,26 @@
+# Generated by devtools/yamaker (pypi).
+
+PY2_LIBRARY()
+
+VERSION(0.10.0)
+
+LICENSE(BSD-3-Clause)
+
+PEERDIR(
+ contrib/python/six
+)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ cycler.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/cycler/py2/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
diff --git a/contrib/python/cycler/py3/.dist-info/METADATA b/contrib/python/cycler/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..e81ab4fa3c
--- /dev/null
+++ b/contrib/python/cycler/py3/.dist-info/METADATA
@@ -0,0 +1,78 @@
+Metadata-Version: 2.1
+Name: cycler
+Version: 0.12.1
+Summary: Composable style cycles
+Author-email: Thomas A Caswell <matplotlib-users@python.org>
+License: Copyright (c) 2015, matplotlib project
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the matplotlib project nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Project-URL: homepage, https://matplotlib.org/cycler/
+Project-URL: repository, https://github.com/matplotlib/cycler
+Keywords: cycle kwargs
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Development Status :: 4 - Beta
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.8
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Provides-Extra: docs
+Requires-Dist: ipython ; extra == 'docs'
+Requires-Dist: matplotlib ; extra == 'docs'
+Requires-Dist: numpydoc ; extra == 'docs'
+Requires-Dist: sphinx ; extra == 'docs'
+Provides-Extra: tests
+Requires-Dist: pytest ; extra == 'tests'
+Requires-Dist: pytest-cov ; extra == 'tests'
+Requires-Dist: pytest-xdist ; extra == 'tests'
+
+|PyPi|_ |Conda|_ |Supported Python versions|_ |GitHub Actions|_ |Codecov|_
+
+.. |PyPi| image:: https://img.shields.io/pypi/v/cycler.svg?style=flat
+.. _PyPi: https://pypi.python.org/pypi/cycler
+
+.. |Conda| image:: https://img.shields.io/conda/v/conda-forge/cycler
+.. _Conda: https://anaconda.org/conda-forge/cycler
+
+.. |Supported Python versions| image:: https://img.shields.io/pypi/pyversions/cycler.svg
+.. _Supported Python versions: https://pypi.python.org/pypi/cycler
+
+.. |GitHub Actions| image:: https://github.com/matplotlib/cycler/actions/workflows/tests.yml/badge.svg
+.. _GitHub Actions: https://github.com/matplotlib/cycler/actions
+
+.. |Codecov| image:: https://codecov.io/github/matplotlib/cycler/badge.svg?branch=main&service=github
+.. _Codecov: https://codecov.io/github/matplotlib/cycler?branch=main
+
+cycler: composable cycles
+=========================
+
+Docs: https://matplotlib.org/cycler/
diff --git a/contrib/python/cycler/py3/.dist-info/top_level.txt b/contrib/python/cycler/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..22546440fa
--- /dev/null
+++ b/contrib/python/cycler/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+cycler
diff --git a/contrib/python/cycler/py3/LICENSE b/contrib/python/cycler/py3/LICENSE
new file mode 100644
index 0000000000..d41d808995
--- /dev/null
+++ b/contrib/python/cycler/py3/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2015, matplotlib project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the matplotlib project nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/contrib/python/cycler/py3/README.rst b/contrib/python/cycler/py3/README.rst
new file mode 100644
index 0000000000..18712c973b
--- /dev/null
+++ b/contrib/python/cycler/py3/README.rst
@@ -0,0 +1,21 @@
+|PyPi|_ |Conda|_ |Supported Python versions|_ |GitHub Actions|_ |Codecov|_
+
+.. |PyPi| image:: https://img.shields.io/pypi/v/cycler.svg?style=flat
+.. _PyPi: https://pypi.python.org/pypi/cycler
+
+.. |Conda| image:: https://img.shields.io/conda/v/conda-forge/cycler
+.. _Conda: https://anaconda.org/conda-forge/cycler
+
+.. |Supported Python versions| image:: https://img.shields.io/pypi/pyversions/cycler.svg
+.. _Supported Python versions: https://pypi.python.org/pypi/cycler
+
+.. |GitHub Actions| image:: https://github.com/matplotlib/cycler/actions/workflows/tests.yml/badge.svg
+.. _GitHub Actions: https://github.com/matplotlib/cycler/actions
+
+.. |Codecov| image:: https://codecov.io/github/matplotlib/cycler/badge.svg?branch=main&service=github
+.. _Codecov: https://codecov.io/github/matplotlib/cycler?branch=main
+
+cycler: composable cycles
+=========================
+
+Docs: https://matplotlib.org/cycler/
diff --git a/contrib/python/cycler/py3/cycler/__init__.py b/contrib/python/cycler/py3/cycler/__init__.py
new file mode 100644
index 0000000000..97949547ad
--- /dev/null
+++ b/contrib/python/cycler/py3/cycler/__init__.py
@@ -0,0 +1,573 @@
+"""
+Cycler
+======
+
+Cycling through combinations of values, producing dictionaries.
+
+You can add cyclers::
+
+ from cycler import cycler
+ cc = (cycler(color=list('rgb')) +
+ cycler(linestyle=['-', '--', '-.']))
+ for d in cc:
+ print(d)
+
+Results in::
+
+ {'color': 'r', 'linestyle': '-'}
+ {'color': 'g', 'linestyle': '--'}
+ {'color': 'b', 'linestyle': '-.'}
+
+
+You can multiply cyclers::
+
+ from cycler import cycler
+ cc = (cycler(color=list('rgb')) *
+ cycler(linestyle=['-', '--', '-.']))
+ for d in cc:
+ print(d)
+
+Results in::
+
+ {'color': 'r', 'linestyle': '-'}
+ {'color': 'r', 'linestyle': '--'}
+ {'color': 'r', 'linestyle': '-.'}
+ {'color': 'g', 'linestyle': '-'}
+ {'color': 'g', 'linestyle': '--'}
+ {'color': 'g', 'linestyle': '-.'}
+ {'color': 'b', 'linestyle': '-'}
+ {'color': 'b', 'linestyle': '--'}
+ {'color': 'b', 'linestyle': '-.'}
+"""
+
+
+from __future__ import annotations
+
+from collections.abc import Hashable, Iterable, Generator
+import copy
+from functools import reduce
+from itertools import product, cycle
+from operator import mul, add
+# Dict, List, Union required for runtime cast calls
+from typing import TypeVar, Generic, Callable, Union, Dict, List, Any, overload, cast
+
+__version__ = "0.12.1"
+
+K = TypeVar("K", bound=Hashable)
+L = TypeVar("L", bound=Hashable)
+V = TypeVar("V")
+U = TypeVar("U")
+
+
+def _process_keys(
+ left: Cycler[K, V] | Iterable[dict[K, V]],
+ right: Cycler[K, V] | Iterable[dict[K, V]] | None,
+) -> set[K]:
+ """
+ Helper function to compose cycler keys.
+
+ Parameters
+ ----------
+ left, right : iterable of dictionaries or None
+ The cyclers to be composed.
+
+ Returns
+ -------
+ keys : set
+ The keys in the composition of the two cyclers.
+ """
+ l_peek: dict[K, V] = next(iter(left)) if left != [] else {}
+ r_peek: dict[K, V] = next(iter(right)) if right is not None else {}
+ l_key: set[K] = set(l_peek.keys())
+ r_key: set[K] = set(r_peek.keys())
+ if l_key & r_key:
+ raise ValueError("Can not compose overlapping cycles")
+ return l_key | r_key
+
+
+def concat(left: Cycler[K, V], right: Cycler[K, U]) -> Cycler[K, V | U]:
+ r"""
+ Concatenate `Cycler`\s, as if chained using `itertools.chain`.
+
+ The keys must match exactly.
+
+ Examples
+ --------
+ >>> num = cycler('a', range(3))
+ >>> let = cycler('a', 'abc')
+ >>> num.concat(let)
+ cycler('a', [0, 1, 2, 'a', 'b', 'c'])
+
+ Returns
+ -------
+ `Cycler`
+ The concatenated cycler.
+ """
+ if left.keys != right.keys:
+ raise ValueError(
+ "Keys do not match:\n"
+ "\tIntersection: {both!r}\n"
+ "\tDisjoint: {just_one!r}".format(
+ both=left.keys & right.keys, just_one=left.keys ^ right.keys
+ )
+ )
+ _l = cast(Dict[K, List[Union[V, U]]], left.by_key())
+ _r = cast(Dict[K, List[Union[V, U]]], right.by_key())
+ return reduce(add, (_cycler(k, _l[k] + _r[k]) for k in left.keys))
+
+
+class Cycler(Generic[K, V]):
+ """
+ Composable cycles.
+
+ This class has compositions methods:
+
+ ``+``
+ for 'inner' products (zip)
+
+ ``+=``
+ in-place ``+``
+
+ ``*``
+ for outer products (`itertools.product`) and integer multiplication
+
+ ``*=``
+ in-place ``*``
+
+ and supports basic slicing via ``[]``.
+
+ Parameters
+ ----------
+ left, right : Cycler or None
+ The 'left' and 'right' cyclers.
+ op : func or None
+ Function which composes the 'left' and 'right' cyclers.
+ """
+
+ def __call__(self):
+ return cycle(self)
+
+ def __init__(
+ self,
+ left: Cycler[K, V] | Iterable[dict[K, V]] | None,
+ right: Cycler[K, V] | None = None,
+ op: Any = None,
+ ):
+ """
+ Semi-private init.
+
+ Do not use this directly, use `cycler` function instead.
+ """
+ if isinstance(left, Cycler):
+ self._left: Cycler[K, V] | list[dict[K, V]] = Cycler(
+ left._left, left._right, left._op
+ )
+ elif left is not None:
+ # Need to copy the dictionary or else that will be a residual
+ # mutable that could lead to strange errors
+ self._left = [copy.copy(v) for v in left]
+ else:
+ self._left = []
+
+ if isinstance(right, Cycler):
+ self._right: Cycler[K, V] | None = Cycler(
+ right._left, right._right, right._op
+ )
+ else:
+ self._right = None
+
+ self._keys: set[K] = _process_keys(self._left, self._right)
+ self._op: Any = op
+
+ def __contains__(self, k):
+ return k in self._keys
+
+ @property
+ def keys(self) -> set[K]:
+ """The keys this Cycler knows about."""
+ return set(self._keys)
+
+ def change_key(self, old: K, new: K) -> None:
+ """
+ Change a key in this cycler to a new name.
+ Modification is performed in-place.
+
+ Does nothing if the old key is the same as the new key.
+ Raises a ValueError if the new key is already a key.
+ Raises a KeyError if the old key isn't a key.
+ """
+ if old == new:
+ return
+ if new in self._keys:
+ raise ValueError(
+ f"Can't replace {old} with {new}, {new} is already a key"
+ )
+ if old not in self._keys:
+ raise KeyError(
+ f"Can't replace {old} with {new}, {old} is not a key"
+ )
+
+ self._keys.remove(old)
+ self._keys.add(new)
+
+ if self._right is not None and old in self._right.keys:
+ self._right.change_key(old, new)
+
+ # self._left should always be non-None
+ # if self._keys is non-empty.
+ elif isinstance(self._left, Cycler):
+ self._left.change_key(old, new)
+ else:
+ # It should be completely safe at this point to
+ # assume that the old key can be found in each
+ # iteration.
+ self._left = [{new: entry[old]} for entry in self._left]
+
+ @classmethod
+ def _from_iter(cls, label: K, itr: Iterable[V]) -> Cycler[K, V]:
+ """
+ Class method to create 'base' Cycler objects
+ that do not have a 'right' or 'op' and for which
+ the 'left' object is not another Cycler.
+
+ Parameters
+ ----------
+ label : hashable
+ The property key.
+
+ itr : iterable
+ Finite length iterable of the property values.
+
+ Returns
+ -------
+ `Cycler`
+ New 'base' cycler.
+ """
+ ret: Cycler[K, V] = cls(None)
+ ret._left = list({label: v} for v in itr)
+ ret._keys = {label}
+ return ret
+
+ def __getitem__(self, key: slice) -> Cycler[K, V]:
+ # TODO : maybe add numpy style fancy slicing
+ if isinstance(key, slice):
+ trans = self.by_key()
+ return reduce(add, (_cycler(k, v[key]) for k, v in trans.items()))
+ else:
+ raise ValueError("Can only use slices with Cycler.__getitem__")
+
+ def __iter__(self) -> Generator[dict[K, V], None, None]:
+ if self._right is None:
+ for left in self._left:
+ yield dict(left)
+ else:
+ if self._op is None:
+ raise TypeError(
+ "Operation cannot be None when both left and right are defined"
+ )
+ for a, b in self._op(self._left, self._right):
+ out = {}
+ out.update(a)
+ out.update(b)
+ yield out
+
+ def __add__(self, other: Cycler[L, U]) -> Cycler[K | L, V | U]:
+ """
+ Pair-wise combine two equal length cyclers (zip).
+
+ Parameters
+ ----------
+ other : Cycler
+ """
+ if len(self) != len(other):
+ raise ValueError(
+ f"Can only add equal length cycles, not {len(self)} and {len(other)}"
+ )
+ return Cycler(
+ cast(Cycler[Union[K, L], Union[V, U]], self),
+ cast(Cycler[Union[K, L], Union[V, U]], other),
+ zip
+ )
+
+ @overload
+ def __mul__(self, other: Cycler[L, U]) -> Cycler[K | L, V | U]:
+ ...
+
+ @overload
+ def __mul__(self, other: int) -> Cycler[K, V]:
+ ...
+
+ def __mul__(self, other):
+ """
+ Outer product of two cyclers (`itertools.product`) or integer
+ multiplication.
+
+ Parameters
+ ----------
+ other : Cycler or int
+ """
+ if isinstance(other, Cycler):
+ return Cycler(
+ cast(Cycler[Union[K, L], Union[V, U]], self),
+ cast(Cycler[Union[K, L], Union[V, U]], other),
+ product
+ )
+ elif isinstance(other, int):
+ trans = self.by_key()
+ return reduce(
+ add, (_cycler(k, v * other) for k, v in trans.items())
+ )
+ else:
+ return NotImplemented
+
+ @overload
+ def __rmul__(self, other: Cycler[L, U]) -> Cycler[K | L, V | U]:
+ ...
+
+ @overload
+ def __rmul__(self, other: int) -> Cycler[K, V]:
+ ...
+
+ def __rmul__(self, other):
+ return self * other
+
+ def __len__(self) -> int:
+ op_dict: dict[Callable, Callable[[int, int], int]] = {zip: min, product: mul}
+ if self._right is None:
+ return len(self._left)
+ l_len = len(self._left)
+ r_len = len(self._right)
+ return op_dict[self._op](l_len, r_len)
+
+ # iadd and imul do not exapand the the type as the returns must be consistent with
+ # self, thus they flag as inconsistent with add/mul
+ def __iadd__(self, other: Cycler[K, V]) -> Cycler[K, V]: # type: ignore[misc]
+ """
+ In-place pair-wise combine two equal length cyclers (zip).
+
+ Parameters
+ ----------
+ other : Cycler
+ """
+ if not isinstance(other, Cycler):
+ raise TypeError("Cannot += with a non-Cycler object")
+ # True shallow copy of self is fine since this is in-place
+ old_self = copy.copy(self)
+ self._keys = _process_keys(old_self, other)
+ self._left = old_self
+ self._op = zip
+ self._right = Cycler(other._left, other._right, other._op)
+ return self
+
+ def __imul__(self, other: Cycler[K, V] | int) -> Cycler[K, V]: # type: ignore[misc]
+ """
+ In-place outer product of two cyclers (`itertools.product`).
+
+ Parameters
+ ----------
+ other : Cycler
+ """
+ if not isinstance(other, Cycler):
+ raise TypeError("Cannot *= with a non-Cycler object")
+ # True shallow copy of self is fine since this is in-place
+ old_self = copy.copy(self)
+ self._keys = _process_keys(old_self, other)
+ self._left = old_self
+ self._op = product
+ self._right = Cycler(other._left, other._right, other._op)
+ return self
+
+ def __eq__(self, other: object) -> bool:
+ if not isinstance(other, Cycler):
+ return False
+ if len(self) != len(other):
+ return False
+ if self.keys ^ other.keys:
+ return False
+ return all(a == b for a, b in zip(self, other))
+
+ __hash__ = None # type: ignore
+
+ def __repr__(self) -> str:
+ op_map = {zip: "+", product: "*"}
+ if self._right is None:
+ lab = self.keys.pop()
+ itr = list(v[lab] for v in self)
+ return f"cycler({lab!r}, {itr!r})"
+ else:
+ op = op_map.get(self._op, "?")
+ msg = "({left!r} {op} {right!r})"
+ return msg.format(left=self._left, op=op, right=self._right)
+
+ def _repr_html_(self) -> str:
+ # an table showing the value of each key through a full cycle
+ output = "<table>"
+ sorted_keys = sorted(self.keys, key=repr)
+ for key in sorted_keys:
+ output += f"<th>{key!r}</th>"
+ for d in iter(self):
+ output += "<tr>"
+ for k in sorted_keys:
+ output += f"<td>{d[k]!r}</td>"
+ output += "</tr>"
+ output += "</table>"
+ return output
+
+ def by_key(self) -> dict[K, list[V]]:
+ """
+ Values by key.
+
+ This returns the transposed values of the cycler. Iterating
+ over a `Cycler` yields dicts with a single value for each key,
+ this method returns a `dict` of `list` which are the values
+ for the given key.
+
+ The returned value can be used to create an equivalent `Cycler`
+ using only `+`.
+
+ Returns
+ -------
+ transpose : dict
+ dict of lists of the values for each key.
+ """
+
+ # TODO : sort out if this is a bottle neck, if there is a better way
+ # and if we care.
+
+ keys = self.keys
+ out: dict[K, list[V]] = {k: list() for k in keys}
+
+ for d in self:
+ for k in keys:
+ out[k].append(d[k])
+ return out
+
+ # for back compatibility
+ _transpose = by_key
+
+ def simplify(self) -> Cycler[K, V]:
+ """
+ Simplify the cycler into a sum (but no products) of cyclers.
+
+ Returns
+ -------
+ simple : Cycler
+ """
+ # TODO: sort out if it is worth the effort to make sure this is
+ # balanced. Currently it is is
+ # (((a + b) + c) + d) vs
+ # ((a + b) + (c + d))
+ # I would believe that there is some performance implications
+ trans = self.by_key()
+ return reduce(add, (_cycler(k, v) for k, v in trans.items()))
+
+ concat = concat
+
+
+@overload
+def cycler(arg: Cycler[K, V]) -> Cycler[K, V]:
+ ...
+
+
+@overload
+def cycler(**kwargs: Iterable[V]) -> Cycler[str, V]:
+ ...
+
+
+@overload
+def cycler(label: K, itr: Iterable[V]) -> Cycler[K, V]:
+ ...
+
+
+def cycler(*args, **kwargs):
+ """
+ Create a new `Cycler` object from a single positional argument,
+ a pair of positional arguments, or the combination of keyword arguments.
+
+ cycler(arg)
+ cycler(label1=itr1[, label2=iter2[, ...]])
+ cycler(label, itr)
+
+ Form 1 simply copies a given `Cycler` object.
+
+ Form 2 composes a `Cycler` as an inner product of the
+ pairs of keyword arguments. In other words, all of the
+ iterables are cycled simultaneously, as if through zip().
+
+ Form 3 creates a `Cycler` from a label and an iterable.
+ This is useful for when the label cannot be a keyword argument
+ (e.g., an integer or a name that has a space in it).
+
+ Parameters
+ ----------
+ arg : Cycler
+ Copy constructor for Cycler (does a shallow copy of iterables).
+ label : name
+ The property key. In the 2-arg form of the function,
+ the label can be any hashable object. In the keyword argument
+ form of the function, it must be a valid python identifier.
+ itr : iterable
+ Finite length iterable of the property values.
+ Can be a single-property `Cycler` that would
+ be like a key change, but as a shallow copy.
+
+ Returns
+ -------
+ cycler : Cycler
+ New `Cycler` for the given property
+
+ """
+ if args and kwargs:
+ raise TypeError(
+ "cycler() can only accept positional OR keyword arguments -- not both."
+ )
+
+ if len(args) == 1:
+ if not isinstance(args[0], Cycler):
+ raise TypeError(
+ "If only one positional argument given, it must "
+ "be a Cycler instance."
+ )
+ return Cycler(args[0])
+ elif len(args) == 2:
+ return _cycler(*args)
+ elif len(args) > 2:
+ raise TypeError(
+ "Only a single Cycler can be accepted as the lone "
+ "positional argument. Use keyword arguments instead."
+ )
+
+ if kwargs:
+ return reduce(add, (_cycler(k, v) for k, v in kwargs.items()))
+
+ raise TypeError("Must have at least a positional OR keyword arguments")
+
+
+def _cycler(label: K, itr: Iterable[V]) -> Cycler[K, V]:
+ """
+ Create a new `Cycler` object from a property name and iterable of values.
+
+ Parameters
+ ----------
+ label : hashable
+ The property key.
+ itr : iterable
+ Finite length iterable of the property values.
+
+ Returns
+ -------
+ cycler : Cycler
+ New `Cycler` for the given property
+ """
+ if isinstance(itr, Cycler):
+ keys = itr.keys
+ if len(keys) != 1:
+ msg = "Can not create Cycler from a multi-property Cycler"
+ raise ValueError(msg)
+
+ lab = keys.pop()
+ # Doesn't need to be a new list because
+ # _from_iter() will be creating that new list anyway.
+ itr = (v[lab] for v in itr)
+
+ return Cycler._from_iter(label, itr)
diff --git a/contrib/python/cycler/py3/cycler/py.typed b/contrib/python/cycler/py3/cycler/py.typed
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/cycler/py3/cycler/py.typed
diff --git a/contrib/python/cycler/py3/ya.make b/contrib/python/cycler/py3/ya.make
new file mode 100644
index 0000000000..49b9ed63da
--- /dev/null
+++ b/contrib/python/cycler/py3/ya.make
@@ -0,0 +1,23 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(0.12.1)
+
+LICENSE(BSD-3-Clause)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ cycler/__init__.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/cycler/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+ cycler/py.typed
+)
+
+END()
diff --git a/contrib/python/cycler/ya.make b/contrib/python/cycler/ya.make
new file mode 100644
index 0000000000..c8bf71a00e
--- /dev/null
+++ b/contrib/python/cycler/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/cycler/py2)
+ELSE()
+ PEERDIR(contrib/python/cycler/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)