From b4156045a3b4fdca61f79de3254854cc955c3ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez=20Novoa?= <49853152+chopan050@users.noreply.github.com> Date: Sun, 19 May 2024 23:49:52 -0400 Subject: [PATCH 1/8] Optimized `manim.utils.bezier.is_closed()` (#3768) * Optimized manim.utils.bezier.is_closed() * oops, that shouldn't have been there * Slightly optimized is_closed() even more * Added doctest for is_closed() --- manim/utils/bezier.py | 73 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/manim/utils/bezier.py b/manim/utils/bezier.py index 3f2bac86bc..05e404248e 100644 --- a/manim/utils/bezier.py +++ b/manim/utils/bezier.py @@ -681,7 +681,78 @@ def get_quadratic_approximation_of_cubic( def is_closed(points: Point3D_Array) -> bool: - return np.allclose(points[0], points[-1]) # type: ignore + """Returns ``True`` if the spline given by ``points`` is closed, by + checking if its first and last points are close to each other, or``False`` + otherwise. + + .. note:: + + This function reimplements :meth:`np.allclose`, because repeated + calling of :meth:`np.allclose` for only 2 points is inefficient. + + Parameters + ---------- + points + An array of points defining a spline. + + Returns + ------- + :class:`bool` + Whether the first and last points of the array are close enough or not + to be considered the same, thus considering the defined spline as + closed. + + Examples + -------- + .. code-block:: pycon + + >>> import numpy as np + >>> from manim import is_closed + >>> is_closed( + ... np.array( + ... [ + ... [0, 0, 0], + ... [1, 2, 3], + ... [3, 2, 1], + ... [0, 0, 0], + ... ] + ... ) + ... ) + True + >>> is_closed( + ... np.array( + ... [ + ... [0, 0, 0], + ... [1, 2, 3], + ... [3, 2, 1], + ... [1e-10, 1e-10, 1e-10], + ... ] + ... ) + ... ) + True + >>> is_closed( + ... np.array( + ... [ + ... [0, 0, 0], + ... [1, 2, 3], + ... [3, 2, 1], + ... [1e-2, 1e-2, 1e-2], + ... ] + ... ) + ... ) + False + """ + start, end = points[0], points[-1] + rtol = 1e-5 + atol = 1e-8 + tolerance = atol + rtol * start + if abs(end[0] - start[0]) > tolerance[0]: + return False + if abs(end[1] - start[1]) > tolerance[1]: + return False + if abs(end[2] - start[2]) > tolerance[2]: + return False + return True def proportions_along_bezier_curve_for_point( From 7b841c27cc33e5d4340486728fbd5287ce9935fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez=20Novoa?= <49853152+chopan050@users.noreply.github.com> Date: Tue, 21 May 2024 01:26:59 -0400 Subject: [PATCH 2/8] =?UTF-8?q?Created=20and=20optimized=20B=C3=A9zier=20s?= =?UTF-8?q?plitting=20functions=20such=20as=20`partial=5Fbezier=5Fpoints()?= =?UTF-8?q?`=20in=20`manim.utils.bezier`=20(#3766)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Optimized manim.utils.partial_bezier_points() * Added split_bezier, subdivide_bezier and bezier_remap, and tests * Use bezier_remap() in VMobject and OpenGLVMobject() * Note that partial_bezier_points is similar to calling split_bezier twice --- .../opengl/opengl_vectorized_mobject.py | 53 +- manim/mobject/types/vectorized_mobject.py | 40 +- manim/utils/bezier.py | 882 +++++++++++++++--- tests/module/utils/_split_matrices.py | 215 +++++ tests/module/utils/_subdivision_matrices.py | 167 ++++ tests/module/utils/test_bezier.py | 97 ++ 6 files changed, 1270 insertions(+), 184 deletions(-) create mode 100644 tests/module/utils/_split_matrices.py create mode 100644 tests/module/utils/_subdivision_matrices.py create mode 100644 tests/module/utils/test_bezier.py diff --git a/manim/mobject/opengl/opengl_vectorized_mobject.py b/manim/mobject/opengl/opengl_vectorized_mobject.py index d668e3a2b1..41d6aac084 100644 --- a/manim/mobject/opengl/opengl_vectorized_mobject.py +++ b/manim/mobject/opengl/opengl_vectorized_mobject.py @@ -15,13 +15,13 @@ from manim.renderer.shader_wrapper import ShaderWrapper from manim.utils.bezier import ( bezier, + bezier_remap, get_quadratic_approximation_of_cubic, get_smooth_cubic_bezier_handle_points, integer_interpolate, interpolate, - partial_quadratic_bezier_points, + partial_bezier_points, proportions_along_bezier_curve_for_point, - quadratic_bezier_remap, ) from manim.utils.color import BLACK, WHITE, ManimColor, ParsableManimColor from manim.utils.config_ops import _Data @@ -555,7 +555,7 @@ def subdivide_sharp_curves(self, angle_threshold=30 * DEGREES, recurse=True): alphas = np.linspace(0, 1, n + 1) new_points.extend( [ - partial_quadratic_bezier_points(tup, a1, a2) + partial_bezier_points(tup, a1, a2) for a1, a2 in zip(alphas, alphas[1:]) ], ) @@ -1275,33 +1275,12 @@ def insert_n_curves_to_point_list(self, n: int, points: np.ndarray) -> np.ndarra if len(points) == 1: return np.repeat(points, nppc * n, 0) - bezier_groups = self.get_bezier_tuples_from_points(points) - norms = np.array([np.linalg.norm(bg[nppc - 1] - bg[0]) for bg in bezier_groups]) - total_norm = sum(norms) - # Calculate insertions per curve (ipc) - if total_norm < 1e-6: - ipc = [n] + [0] * (len(bezier_groups) - 1) - else: - ipc = np.round(n * norms / sum(norms)).astype(int) - - diff = n - sum(ipc) - for _ in range(diff): - ipc[np.argmin(ipc)] += 1 - for _ in range(-diff): - ipc[np.argmax(ipc)] -= 1 - - new_length = sum(x + 1 for x in ipc) - new_points = np.empty((new_length, nppc, 3)) - i = 0 - for group, n_inserts in zip(bezier_groups, ipc): - # What was once a single quadratic curve defined - # by "group" will now be broken into n_inserts + 1 - # smaller quadratic curves - alphas = np.linspace(0, 1, n_inserts + 2) - for a1, a2 in zip(alphas, alphas[1:]): - new_points[i] = partial_quadratic_bezier_points(group, a1, a2) - i = i + 1 - return np.vstack(new_points) + bezier_tuples = self.get_bezier_tuples_from_points(points) + current_number_of_curves = len(bezier_tuples) + new_number_of_curves = current_number_of_curves + n + new_bezier_tuples = bezier_remap(bezier_tuples, new_number_of_curves) + new_points = new_bezier_tuples.reshape(-1, 3) + return new_points def interpolate(self, mobject1, mobject2, alpha, *args, **kwargs): super().interpolate(mobject1, mobject2, alpha, *args, **kwargs) @@ -1354,7 +1333,7 @@ def pointwise_become_partial( return self if lower_index == upper_index: self.append_points( - partial_quadratic_bezier_points( + partial_bezier_points( bezier_triplets[lower_index], lower_residue, upper_residue, @@ -1362,24 +1341,18 @@ def pointwise_become_partial( ) else: self.append_points( - partial_quadratic_bezier_points( - bezier_triplets[lower_index], lower_residue, 1 - ), + partial_bezier_points(bezier_triplets[lower_index], lower_residue, 1), ) inner_points = bezier_triplets[lower_index + 1 : upper_index] if len(inner_points) > 0: if remap: - new_triplets = quadratic_bezier_remap( - inner_points, num_quadratics - 2 - ) + new_triplets = bezier_remap(inner_points, num_quadratics - 2) else: new_triplets = bezier_triplets self.append_points(np.asarray(new_triplets).reshape(-1, 3)) self.append_points( - partial_quadratic_bezier_points( - bezier_triplets[upper_index], 0, upper_residue - ), + partial_bezier_points(bezier_triplets[upper_index], 0, upper_residue), ) return self diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index cf7ce9a6b4..deb3690620 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -30,6 +30,7 @@ ) from manim.utils.bezier import ( bezier, + bezier_remap, get_smooth_handle_points, integer_interpolate, interpolate, @@ -1693,40 +1694,11 @@ def insert_n_curves_to_point_list( if len(points) == 1: nppcc = self.n_points_per_cubic_curve return np.repeat(points, nppcc * n, 0) - bezier_quads = self.get_cubic_bezier_tuples_from_points(points) - curr_num = len(bezier_quads) - target_num = curr_num + n - # This is an array with values ranging from 0 - # up to curr_num, with repeats such that - # it's total length is target_num. For example, - # with curr_num = 10, target_num = 15, this would - # be [0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9] - repeat_indices = (np.arange(target_num, dtype="i") * curr_num) // target_num - - # If the nth term of this list is k, it means - # that the nth curve of our path should be split - # into k pieces. - # In the above example our array had the following elements - # [0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9] - # We have two 0s, one 1, two 2s and so on. - # The split factors array would hence be: - # [2, 1, 2, 1, 2, 1, 2, 1, 2, 1] - split_factors = np.zeros(curr_num, dtype="i") - for val in repeat_indices: - split_factors[val] += 1 - - new_points = np.zeros((0, self.dim)) - for quad, sf in zip(bezier_quads, split_factors): - # What was once a single cubic curve defined - # by "quad" will now be broken into sf - # smaller cubic curves - alphas = np.linspace(0, 1, sf + 1) - for a1, a2 in zip(alphas, alphas[1:]): - new_points = np.append( - new_points, - partial_bezier_points(quad, a1, a2), - axis=0, - ) + bezier_tuples = self.get_cubic_bezier_tuples_from_points(points) + current_number_of_curves = len(bezier_tuples) + new_number_of_curves = current_number_of_curves + n + new_bezier_tuples = bezier_remap(bezier_tuples, new_number_of_curves) + new_points = new_bezier_tuples.reshape(-1, 3) return new_points def align_rgbas(self, vmobject: VMobject) -> Self: diff --git a/manim/utils/bezier.py b/manim/utils/bezier.py index 05e404248e..709f8b3bf4 100644 --- a/manim/utils/bezier.py +++ b/manim/utils/bezier.py @@ -16,7 +16,9 @@ __all__ = [ "bezier", "partial_bezier_points", - "partial_quadratic_bezier_points", + "split_bezier", + "subdivide_bezier", + "bezier_remap", "interpolate", "integer_interpolate", "mid", @@ -86,12 +88,120 @@ def bezier( ) -# !TODO: This function has still a weird implementation with the overlapping points def partial_bezier_points(points: BezierPoints, a: float, b: float) -> BezierPoints: - """Given an array of points which define bezier curve, and two numbers 0<=a BezierPoi Returns ------- - np.ndarray - Set of points defining the partial bezier curve. + :class:`~.BezierPoints` + An array containing the control points defining the partial Bézier curve. """ - _len = len(points) + # Border cases if a == 1: - return np.asarray([points[-1]] * _len, dtype=PointDType) + arr = np.array(points) + arr[:] = arr[-1] + return arr + if b == 0: + arr = np.array(points) + arr[:] = arr[0] + return arr - a_to_1 = np.asarray( - [bezier(points[i:])(a) for i in range(_len)], - dtype=PointDType, - ) - end_prop = (b - a) / (1.0 - a) - return np.asarray( - [bezier(a_to_1[: i + 1])(end_prop) for i in range(_len)], - dtype=PointDType, - ) + points = np.asarray(points) + degree = points.shape[0] - 1 + if degree == 3: + ma, mb = 1 - a, 1 - b + a2, b2, ma2, mb2 = a * a, b * b, ma * ma, mb * mb + a3, b3, ma3, mb3 = a2 * a, b2 * b, ma2 * ma, mb2 * mb -# Shortened version of partial_bezier_points just for quadratics, -# since this is called a fair amount -def partial_quadratic_bezier_points( - points: QuadraticBezierPoints, a: float, b: float -) -> QuadraticBezierPoints: - if a == 1: - return np.asarray(3 * [points[-1]]) + portion_matrix = np.array( + [ + [ma3, 3 * ma2 * a, 3 * ma * a2, a3], + [ma2 * mb, 2 * ma * a * mb + ma2 * b, a2 * mb + 2 * ma * a * b, a2 * b], + [ma * mb2, a * mb2 + 2 * ma * mb * b, 2 * a * mb * b + ma * b2, a * b2], + [mb3, 3 * mb2 * b, 3 * mb * b2, b3], + ] + ) + return portion_matrix @ points + + if degree == 2: + ma, mb = 1 - a, 1 - b - def curve(t: float) -> Point3D: - return np.asarray( - points[0] * (1 - t) * (1 - t) - + 2 * points[1] * t * (1 - t) - + points[2] * t * t + portion_matrix = np.array( + [ + [ma * ma, 2 * a * ma, a * a], + [ma * mb, a * mb + ma * b, a * b], + [mb * mb, 2 * b * mb, b * b], + ] ) + return portion_matrix @ points - # bezier(points) - h0 = curve(a) if a > 0 else points[0] - h2 = curve(b) if b < 1 else points[2] - h1_prime = (1 - a) * points[1] + a * points[2] - end_prop = (b - a) / (1.0 - a) - h1 = (1 - end_prop) * h0 + end_prop * h1_prime - return np.asarray((h0, h1, h2)) + if degree == 1: + direction = points[1] - points[0] + return np.array( + [ + points[0] + a * direction, + points[0] + b * direction, + ] + ) + + if degree == 0: + return points + + # Fallback case for nth degree Béziers + # It is convenient that np.array copies points + arr = np.array(points, dtype=float) + N = arr.shape[0] + + # Current state for an example Bézier curve C0 = [P0, P1, P2, P3]: + # arr = [P0, P1, P2, P3] + if a != 0: + for i in range(1, N): + # 1st iter: arr = [L0(a), L1(a), L2(a), P3] + # 2nd iter: arr = [Q0(a), Q1(a), L2(a), P3] + # 3rd iter: arr = [C0(a), Q1(a), L2(a), P3] + arr[: N - i] += a * (arr[1 : N - i + 1] - arr[: N - i]) + + # For faster calculations we shall define mu = 1 - u = (1 - b) / (1 - a). + # This is because: + # L0'(u) = P0' + u(P1' - P0') + # = (1-u)P0' + uP1' + # = muP0' + (1-mu)P1' + # = P1' + mu(P0' - P1) + # In this way, one can do something similar to the first loop. + # + # Current state: + # arr = [C0(a), Q1(a), L2(a), P3] + # = [P0', P1', P2', P3'] + if b != 1: + mu = (1 - b) / (1 - a) + for i in range(1, N): + # 1st iter: arr = [P0', L0'(u), L1'(u), L2'(u)] + # 2nd iter: arr = [P0', L0'(u), Q0'(u), Q1'(u)] + # 3rd iter: arr = [P0', L0'(u), Q0'(u), C0'(u)] + arr[i:] += mu * (arr[i - 1 : -1] - arr[i:]) + + return arr + + +def split_bezier(points: BezierPoints, t: float) -> Point3D_Array: + r"""Split a Bézier curve at argument ``t`` into two curves. + .. note:: -def split_quadratic_bezier(points: QuadraticBezierPoints, t: float) -> BezierPoints: - """Split a quadratic Bézier curve at argument ``t`` into two quadratic curves. + .. seealso:: + `A Primer on Bézier Curves #10: Splitting curves. Pomax. `_ + + As an example for a cubic Bézier curve, let :math:`p_0, p_1, p_2, p_3` be the points + needed for the curve :math:`C_0 = [p_0, \ p_1, \ p_2, \ p_3]`. + + Define the 3 linear Béziers :math:`L_0, L_1, L_2` as interpolations of :math:`p_0, p_1, p_2, p_3`: + + .. math:: + L_0(t) &= p_0 + t(p_1 - p_0) \\ + L_1(t) &= p_1 + t(p_2 - p_1) \\ + L_2(t) &= p_2 + t(p_3 - p_2) + + Define the 2 quadratic Béziers :math:`Q_0, Q_1` as interpolations of :math:`L_0, L_1, L_2`: + + .. math:: + Q_0(t) &= L_0(t) + t(L_1(t) - L_0(t)) \\ + Q_1(t) &= L_1(t) + t(L_2(t) - L_1(t)) + + Then :math:`C_0` is the following interpolation of :math:`Q_0` and :math:`Q_1`: + + .. math:: + C_0(t) = Q_0(t) + t(Q_1(t) - Q_0(t)) + + Evaluating :math:`C_0` at a value :math:`t=t'` splits :math:`C_0` into two cubic Béziers :math:`H_0` + and :math:`H_1`, defined by some of the points we calculated earlier: + + .. math:: + H_0 &= [p_0, &\ L_0(t'), &\ Q_0(t'), &\ C_0(t') &] \\ + H_1 &= [p_0(t'), &\ Q_1(t'), &\ L_2(t'), &\ p_3 &] + + As the resulting curves are obtained from linear combinations of ``points``, everything can + be encoded into a matrix for efficiency, which is done for Bézier curves of degree up to 3. + + .. seealso:: + `A Primer on Bézier Curves #11: Splitting curves using matrices. Pomax. `_ + + For the simpler case of a quadratic Bézier curve: + + .. math:: + H_0 + &= + \begin{pmatrix} + p_0 \\ + (1-t) p_0 + t p_1 \\ + (1-t)^2 p_0 + 2(1-t)t p_1 + t^2 p_2 \\ + \end{pmatrix} + &= + \begin{pmatrix} + 1 & 0 & 0 \\ + (1-t) & t & 0\\ + (1-t)^2 & 2(1-t)t & t^2 + \end{pmatrix} + \begin{pmatrix} + p_0 \\ + p_1 \\ + p_2 + \end{pmatrix} + \\ + & + \\ + H_1 + &= + \begin{pmatrix} + (1-t)^2 p_0 + 2(1-t)t p_1 + t^2 p_2 \\ + (1-t) p_1 + t p_2 \\ + p_2 + \end{pmatrix} + &= + \begin{pmatrix} + (1-t)^2 & 2(1-t)t & t^2 \\ + 0 & (1-t) & t \\ + 0 & 0 & 1 + \end{pmatrix} + \begin{pmatrix} + p_0 \\ + p_1 \\ + p_2 + \end{pmatrix} + + from where one can define a :math:`(6, 3)` split matrix :math:`S_2` which can multiply + the array of ``points`` to compute the return value: + + .. math:: + S_2 + &= + \begin{pmatrix} + 1 & 0 & 0 \\ + (1-t) & t & 0 \\ + (1-t)^2 & 2(1-t)t & t^2 \\ + (1-t)^2 & 2(1-t)t & t^2 \\ + 0 & (1-t) & t \\ + 0 & 0 & 1 + \end{pmatrix} + \\ + & + \\ + S_2 P + &= + \begin{pmatrix} + 1 & 0 & 0 \\ + (1-t) & t & 0 \\ + (1-t)^2 & 2(1-t)t & t^2 \\ + (1-t)^2 & 2(1-t)t & t^2 \\ + 0 & (1-t) & t \\ + 0 & 0 & 1 + \end{pmatrix} + \begin{pmatrix} + p_0 \\ + p_1 \\ + p_2 + \end{pmatrix} + = + \begin{pmatrix} + \vert \\ + H_0 \\ + \vert \\ + \vert \\ + H_1 \\ + \vert + \end{pmatrix} + + For the previous example with a cubic Bézier curve: + + .. math:: + H_0 + &= + \begin{pmatrix} + p_0 \\ + (1-t) p_0 + t p_1 \\ + (1-t)^2 p_0 + 2(1-t)t p_1 + t^2 p_2 \\ + (1-t)^3 p_0 + 3(1-t)^2 t p_1 + 3(1-t)t^2 p_2 + t^3 p_3 + \end{pmatrix} + &= + \begin{pmatrix} + 1 & 0 & 0 & 0 \\ + (1-t) & t & 0 & 0 \\ + (1-t)^2 & 2(1-t)t & t^2 & 0 \\ + (1-t)^3 & 3(1-t)^2 t & 3(1-t)t^2 & t^3 + \end{pmatrix} + \begin{pmatrix} + p_0 \\ + p_1 \\ + p_2 \\ + p_3 + \end{pmatrix} + \\ + & + \\ + H_1 + &= + \begin{pmatrix} + (1-t)^3 p_0 + 3(1-t)^2 t p_1 + 3(1-t)t^2 p_2 + t^3 p_3 \\ + (1-t)^2 p_1 + 2(1-t)t p_2 + t^2 p_3 \\ + (1-t) p_2 + t p_3 \\ + p_3 + \end{pmatrix} + &= + \begin{pmatrix} + (1-t)^3 & 3(1-t)^2 t & 3(1-t)t^2 & t^3 \\ + 0 & (1-t)^2 & 2(1-t)t & t^2 \\ + 0 & 0 & (1-t) & t \\ + 0 & 0 & 0 & 1 + \end{pmatrix} + \begin{pmatrix} + p_0 \\ + p_1 \\ + p_2 \\ + p_3 + \end{pmatrix} + + from where one can define a :math:`(8, 4)` split matrix :math:`S_3` which can multiply + the array of ``points`` to compute the return value: + + .. math:: + S_3 + &= + \begin{pmatrix} + 1 & 0 & 0 & 0 \\ + (1-t) & t & 0 & 0 \\ + (1-t)^2 & 2(1-t)t & t^2 & 0 \\ + (1-t)^3 & 3(1-t)^2 t & 3(1-t)t^2 & t^3 \\ + (1-t)^3 & 3(1-t)^2 t & 3(1-t)t^2 & t^3 \\ + 0 & (1-t)^2 & 2(1-t)t & t^2 \\ + 0 & 0 & (1-t) & t \\ + 0 & 0 & 0 & 1 + \end{pmatrix} + \\ + & + \\ + S_3 P + &= + \begin{pmatrix} + 1 & 0 & 0 & 0 \\ + (1-t) & t & 0 & 0 \\ + (1-t)^2 & 2(1-t)t & t^2 & 0 \\ + (1-t)^3 & 3(1-t)^2 t & 3(1-t)t^2 & t^3 \\ + (1-t)^3 & 3(1-t)^2 t & 3(1-t)t^2 & t^3 \\ + 0 & (1-t)^2 & 2(1-t)t & t^2 \\ + 0 & 0 & (1-t) & t \\ + 0 & 0 & 0 & 1 + \end{pmatrix} + \begin{pmatrix} + p_0 \\ + p_1 \\ + p_2 \\ + p_3 + \end{pmatrix} + = + \begin{pmatrix} + \vert \\ + H_0 \\ + \vert \\ + \vert \\ + H_1 \\ + \vert + \end{pmatrix} Parameters ---------- points - The control points of the bezier curve - has shape ``[a1, h1, b1]`` + The control points of the Bézier curve. t - The ``t``-value at which to split the Bézier curve + The ``t``-value at which to split the Bézier curve. Returns ------- - The two Bézier curves as a list of tuples, - has the shape ``[a1, h1, b1], [a2, h2, b2]`` + :class:`~.Point3D_Array` + An array containing the control points defining the two Bézier curves. """ - a1, h1, a2 = points - s1 = interpolate(a1, h1, t) - s2 = interpolate(h1, a2, t) - p = interpolate(s1, s2, t) - return np.array((a1, s1, p, p, s2, a2)) + points = np.asarray(points) + N, dim = points.shape + degree = N - 1 + + if degree == 3: + mt = 1 - t + mt2 = mt * mt + mt3 = mt2 * mt + t2 = t * t + t3 = t2 * t + two_mt_t = 2 * mt * t + three_mt2_t = 3 * mt2 * t + three_mt_t2 = 3 * mt * t2 + + # Split matrix S3 explained in the docstring + split_matrix = np.array( + [ + [1, 0, 0, 0], + [mt, t, 0, 0], + [mt2, two_mt_t, t2, 0], + [mt3, three_mt2_t, three_mt_t2, t3], + [mt3, three_mt2_t, three_mt_t2, t3], + [0, mt2, two_mt_t, t2], + [0, 0, mt, t], + [0, 0, 0, 1], + ] + ) + + return split_matrix @ points + + if degree == 2: + mt = 1 - t + mt2 = mt * mt + t2 = t * t + two_tmt = 2 * t * mt + + # Split matrix S2 explained in the docstring + split_matrix = np.array( + [ + [1, 0, 0], + [mt, t, 0], + [mt2, two_tmt, t2], + [mt2, two_tmt, t2], + [0, mt, t], + [0, 0, 1], + ] + ) + + return split_matrix @ points + + if degree == 1: + middle = points[0] + t * (points[1] - points[0]) + return np.array([points[0], middle, middle, points[1]]) + + if degree == 0: + return np.array([points[0], points[0]]) + + # Fallback case for nth degree Béziers + arr = np.empty((2, N, dim)) + arr[1] = points + arr[0, 0] = points[0] + + # Example for a cubic Bézier + # arr[0] = [P0 .. .. ..] + # arr[1] = [P0 P1 P2 P3] + for i in range(1, N): + # 1st iter: arr[1] = [L0 L1 L2 P3] + # 2nd iter: arr[1] = [Q0 Q1 L2 P3] + # 3rd iter: arr[1] = [C0 Q1 L2 P3] + arr[1, : N - i] += t * (arr[1, 1 : N - i + 1] - arr[1, : N - i]) + # 1st iter: arr[0] = [P0 L0 .. ..] + # 2nd iter: arr[0] = [P0 L0 Q0 ..] + # 3rd iter: arr[0] = [P0 L0 Q0 C0] + arr[0, i] = arr[1, 0] + + return arr.reshape(2 * N, dim) + +# Memos explained in subdivide_bezier docstring +SUBDIVISION_MATRICES = [{} for i in range(4)] -def subdivide_quadratic_bezier(points: QuadraticBezierPoints, n: int) -> BezierPoints: - """Subdivide a quadratic Bézier curve into ``n`` subcurves which have the same shape. + +def _get_subdivision_matrix(n_points: int, n_divisions: int) -> MatrixMN: + """Gets the matrix which subdivides a Bézier curve of + ``n_points`` control points into ``n_divisions`` parts. + + Auxiliary function for :func:`subdivide_bezier`. See its + docstrings for an explanation of the matrix build process. + + Parameters + ---------- + n_points + The number of control points of the Bézier curve to + subdivide. This function only handles up to 4 points. + n_divisions + The number of parts to subdivide the Bézier curve into. + + Returns + ------- + MatrixMN + The matrix which, upon multiplying the control points of the + Bézier curve, subdivides it into ``n_divisions`` parts. + """ + if n_points not in (1, 2, 3, 4): + raise NotImplementedError( + "This function does not support subdividing Bézier " + "curves with 0 or more than 4 control points." + ) + + subdivision_matrix = SUBDIVISION_MATRICES[n_points - 1].get(n_divisions, None) + if subdivision_matrix is not None: + return subdivision_matrix + + subdivision_matrix = np.empty((n_points * n_divisions, n_points)) + + # Cubic Bézier + if n_points == 4: + for i in range(n_divisions): + i2 = i * i + i3 = i2 * i + ip1 = i + 1 + ip12 = ip1 * ip1 + ip13 = ip12 * ip1 + nmi = n_divisions - i + nmi2 = nmi * nmi + nmi3 = nmi2 * nmi + nmim1 = nmi - 1 + nmim12 = nmim1 * nmim1 + nmim13 = nmim12 * nmim1 + + subdivision_matrix[4 * i : 4 * (i + 1)] = np.array( + [ + [ + nmi3, + 3 * nmi2 * i, + 3 * nmi * i2, + i3, + ], + [ + nmi2 * nmim1, + 2 * nmi * nmim1 * i + nmi2 * ip1, + nmim1 * i2 + 2 * nmi * i * ip1, + i2 * ip1, + ], + [ + nmi * nmim12, + nmim12 * i + 2 * nmi * nmim1 * ip1, + 2 * nmim1 * i * ip1 + nmi * ip12, + i * ip12, + ], + [ + nmim13, + 3 * nmim12 * ip1, + 3 * nmim1 * ip12, + ip13, + ], + ] + ) + subdivision_matrix /= n_divisions * n_divisions * n_divisions + + # Quadratic Bézier + elif n_points == 3: + for i in range(n_divisions): + ip1 = i + 1 + nmi = n_divisions - i + nmim1 = nmi - 1 + subdivision_matrix[3 * i : 3 * (i + 1)] = np.array( + [ + [nmi * nmi, 2 * i * nmi, i * i], + [nmi * nmim1, i * nmim1 + ip1 * nmi, i * ip1], + [nmim1 * nmim1, 2 * ip1 * nmim1, ip1 * ip1], + ] + ) + subdivision_matrix /= n_divisions * n_divisions + + # Linear Bézier (straight line) + elif n_points == 2: + aux_range = np.arange(n_divisions + 1) + subdivision_matrix[::2, 1] = aux_range[:-1] + subdivision_matrix[1::2, 1] = aux_range[1:] + subdivision_matrix[:, 0] = subdivision_matrix[::-1, 1] + subdivision_matrix /= n_divisions + + # Zero-degree Bézier (single point) + elif n_points == 1: + subdivision_matrix[:] = 1 + + SUBDIVISION_MATRICES[n_points - 1][n_divisions] = subdivision_matrix + return subdivision_matrix + + +def subdivide_bezier(points: BezierPoints, n_divisions: int) -> Point3D_Array: + r"""Subdivide a Bézier curve into :math:`n` subcurves which have the same shape. The points at which the curve is split are located at the - arguments :math:`t = i/n` for :math:`i = 1, ..., n-1`. + arguments :math:`t = \frac{i}{n}`, for :math:`i \in \{1, ..., n-1\}`. + + .. seealso:: + + * See :func:`split_bezier` for an explanation on how to split Bézier curves. + * See :func:`partial_bezier_points` for an extra understanding of this function. + + + .. note:: + The resulting subcurves can be expressed as linear combinations of + ``points``, which can be encoded in a single matrix that is precalculated + for 2nd and 3rd degree Bézier curves. + + As an example for a quadratic Bézier curve: taking inspiration from the + explanation in :func:`partial_bezier_points`, where the following matrix + :math:`P_2` was defined to extract the portion of a quadratic Bézier + curve for :math:`t \in [a, b]`: + + .. math:: + P_2 + = + \begin{pmatrix} + (1-a)^2 & 2(1-a)a & a^2 \\ + (1-a)(1-b) & a(1-b) + (1-a)b & ab \\ + (1-b)^2 & 2(1-b)b & b^2 + \end{pmatrix} + + the plan is to replace :math:`[a, b]` with + :math:`\left[ \frac{i-1}{n}, \frac{i}{n} \right], \ \forall i \in \{1, ..., n\}`. + + As an example for :math:`n = 2` divisions, construct :math:`P_1` for + the interval :math:`\left[ 0, \frac{1}{2} \right]`, and :math:`P_2` for the + interval :math:`\left[ \frac{1}{2}, 1 \right]`: + + .. math:: + P_1 + = + \begin{pmatrix} + 1 & 0 & 0 \\ + 0.5 & 0.5 & 0 \\ + 0.25 & 0.5 & 0.25 + \end{pmatrix} + , + \quad + P_2 + = + \begin{pmatrix} + 0.25 & 0.5 & 0.25 \\ + 0 & 0.5 & 0.5 \\ + 0 & 0 & 1 + \end{pmatrix} + + Therefore, the following :math:`(6, 3)` subdivision matrix :math:`D_2` can be + constructed, which will subdivide an array of ``points`` into 2 parts: + + .. math:: + D_2 + = + \begin{pmatrix} + M_1 \\ + M_2 + \end{pmatrix} + = + \begin{pmatrix} + 1 & 0 & 0 \\ + 0.5 & 0.5 & 0 \\ + 0.25 & 0.5 & 0.25 \\ + 0.25 & 0.5 & 0.25 \\ + 0 & 0.5 & 0.5 \\ + 0 & 0 & 1 + \end{pmatrix} + + For quadratic and cubic Bézier curves, the subdivision matrices are memoized for + efficiency. For higher degree curves, an iterative algorithm inspired by the + one from :func:`split_bezier` is used instead. + + .. image:: /_static/bezier_subdivision_example.png Parameters ---------- points - The control points of the Bézier curve in form ``[a1, h1, b1]`` + The control points of the Bézier curve. - n + n_divisions The number of curves to subdivide the Bézier curve into Returns ------- - The new points for the Bézier curve in the form ``[a1, h1, b1, a2, h2, b2, ...]`` - - .. image:: /_static/bezier_subdivision_example.png - + :class:`~.Point3D_Array` + An array containing the points defining the new :math:`n` subcurves. """ - beziers = np.empty((n, 3, 3)) - current = points - for j in range(0, n): - i = n - j - tmp = split_quadratic_bezier(current, 1 / i) - beziers[j] = tmp[:3] - current = tmp[3:] - return beziers.reshape(-1, 3) + if n_divisions == 1: + return points - -def quadratic_bezier_remap( - triplets: QuadraticBezierPoints_Array, new_number_of_curves: int -) -> QuadraticBezierPoints_Array: - """Remaps the number of curves to a higher amount by splitting bezier curves + points = np.asarray(points) + N, dim = points.shape + + if N <= 4: + subdivision_matrix = _get_subdivision_matrix(N, n_divisions) + return subdivision_matrix @ points + + # Fallback case for an nth degree Bézier: successive splitting + beziers = np.empty((n_divisions, N, dim)) + beziers[-1] = points + for curve_num in range(n_divisions - 1, 0, -1): + curr = beziers[curve_num] + prev = beziers[curve_num - 1] + prev[0] = curr[0] + a = (n_divisions - curve_num) / (n_divisions - curve_num + 1) + # Current state for an example cubic Bézier curve: + # prev = [P0 .. .. ..] + # curr = [P0 P1 P2 P3] + for i in range(1, N): + # 1st iter: curr = [L0 L1 L2 P3] + # 2nd iter: curr = [Q0 Q1 L2 P3] + # 3rd iter: curr = [C0 Q1 L2 P3] + curr[: N - i] += a * (curr[1 : N - i + 1] - curr[: N - i]) + # 1st iter: prev = [P0 L0 .. ..] + # 2nd iter: prev = [P0 L0 Q0 ..] + # 3rd iter: prev = [P0 L0 Q0 C0] + prev[i] = curr[0] + + return beziers.reshape(n_divisions * N, dim) + + +def bezier_remap( + bezier_tuples: BezierPoints_Array, + new_number_of_curves: int, +) -> BezierPoints_Array: + """Subdivides each curve in ``bezier_tuples`` into as many parts as necessary, until the final number of + curves reaches a desired amount, ``new_number_of_curves``. Parameters ---------- - triplets - The triplets of the quadratic bezier curves to be remapped shape(n, 3, 3) + bezier_tuples + An array of multiple Bézier curves of degree :math:`d` to be remapped. The shape of this array + must be ``(current_number_of_curves, nppc, dim)``, where: + * ``current_number_of_curves`` is the current amount of curves in the array ``bezier_tuples``, + * ``nppc`` is the amount of points per curve, such that their degree is ``nppc-1``, and + * ``dim`` is the dimension of the points, usually :math:`3`. new_number_of_curves The number of curves that the output will contain. This needs to be higher than the current number. Returns ------- - The new triplets for the quadratic bezier curves. + :class:`~.BezierPoints_Array` + The new array of shape ``(new_number_of_curves, nppc, dim)``, + containing the new Bézier curves after the remap. """ - difference = new_number_of_curves - len(triplets) - if difference <= 0: - return triplets - new_triplets = np.zeros((new_number_of_curves, 3, 3)) - idx = 0 - for triplet in triplets: - if difference > 0: - tmp_noc = int(np.ceil(difference / len(triplets))) + 1 - tmp = subdivide_quadratic_bezier(triplet, tmp_noc).reshape(-1, 3, 3) - for i in range(tmp_noc): - new_triplets[idx + i] = tmp[i] - difference -= tmp_noc - 1 - idx += tmp_noc - else: - new_triplets[idx] = triplet - idx += 1 - return new_triplets - - """ - This is an alternate version of the function just for documentation purposes - -------- + bezier_tuples = np.asarray(bezier_tuples) + current_number_of_curves, nppc, dim = bezier_tuples.shape + # This is an array with values ranging from 0 + # up to curr_num_curves, with repeats such that + # its total length is target_num_curves. For example, + # with curr_num_curves = 10, target_num_curves = 15, this + # would be [0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9]. + repeat_indices = ( + np.arange(new_number_of_curves, dtype="i") * current_number_of_curves + ) // new_number_of_curves + + # If the nth term of this list is k, it means + # that the nth curve of our path should be split + # into k pieces. + # In the above example our array had the following elements + # [0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9] + # We have two 0s, one 1, two 2s and so on. + # The split factors array would hence be: + # [2, 1, 2, 1, 2, 1, 2, 1, 2, 1] + split_factors = np.zeros(current_number_of_curves, dtype="i") + np.add.at(split_factors, repeat_indices, 1) + + new_tuples = np.empty((new_number_of_curves, nppc, dim)) + index = 0 + for curve, sf in zip(bezier_tuples, split_factors): + new_tuples[index : index + sf] = subdivide_bezier(curve, sf).reshape( + sf, nppc, dim + ) + index += sf - difference = new_number_of_curves - len(triplets) - if difference <= 0: - return triplets - new_triplets = [] - for triplet in triplets: - if difference > 0: - tmp_noc = int(np.ceil(difference / len(triplets))) + 1 - tmp = subdivide_quadratic_bezier(triplet, tmp_noc).reshape(-1, 3, 3) - for i in range(tmp_noc): - new_triplets.append(tmp[i]) - difference -= tmp_noc - 1 - else: - new_triplets.append(triplet) - return new_triplets - """ + return new_tuples # Linear interpolation variants diff --git a/tests/module/utils/_split_matrices.py b/tests/module/utils/_split_matrices.py new file mode 100644 index 0000000000..70c87497e6 --- /dev/null +++ b/tests/module/utils/_split_matrices.py @@ -0,0 +1,215 @@ +import numpy as np + +# Defined because pre-commit is inserting an unacceptable line-break +# between the "1" (or "2") and the "/ 3" +one_third = 1 / 3 +two_thirds = 2 / 3 + + +# Expected values for matrices in split_bezier +SPLIT_MATRICES = { + # For 0-degree Béziers + 0: { + 0: np.array([[1], [1]]), + one_third: np.array([[1], [1]]), + two_thirds: np.array([[1], [1]]), + 1: np.array([[1], [1]]), + }, + # For linear Béziers + 1: { + 0: np.array( + [ + [1, 0], + [1, 0], + [1, 0], + [0, 1], + ] + ), + one_third: np.array( + [ + [3, 0], + [2, 1], + [2, 1], + [0, 3], + ] + ) + / 3, + two_thirds: np.array( + [ + [3, 0], + [1, 2], + [1, 2], + [0, 3], + ] + ) + / 3, + 1: np.array( + [ + [1, 0], + [0, 1], + [0, 1], + [0, 1], + ] + ), + }, + # For quadratic Béziers + 2: { + 0: np.array( + [ + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [0, 1, 0], + [0, 0, 1], + ] + ), + one_third: np.array( + [ + [9, 0, 0], + [6, 3, 0], + [4, 4, 1], + [4, 4, 1], + [0, 6, 3], + [0, 0, 9], + ] + ) + / 9, + two_thirds: np.array( + [ + [9, 0, 0], + [3, 6, 0], + [1, 4, 4], + [1, 4, 4], + [0, 3, 6], + [0, 0, 9], + ] + ) + / 9, + 1: np.array( + [ + [1, 0, 0], + [0, 1, 0], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + ] + ), + }, + # For cubic Béziers + 3: { + 0: np.array( + [ + [1, 0, 0, 0], + [1, 0, 0, 0], + [1, 0, 0, 0], + [1, 0, 0, 0], + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], + ] + ), + one_third: np.array( + [ + [27, 0, 0, 0], + [18, 9, 0, 0], + [12, 12, 3, 0], + [8, 12, 6, 1], + [8, 12, 6, 1], + [0, 12, 12, 3], + [0, 0, 18, 9], + [0, 0, 0, 27], + ] + ) + / 27, + two_thirds: np.array( + [ + [27, 0, 0, 0], + [9, 18, 0, 0], + [3, 12, 12, 0], + [1, 6, 12, 8], + [1, 6, 12, 8], + [0, 3, 12, 12], + [0, 0, 9, 18], + [0, 0, 0, 27], + ] + ) + / 27, + 1: np.array( + [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], + [0, 0, 0, 1], + [0, 0, 0, 1], + [0, 0, 0, 1], + [0, 0, 0, 1], + ] + ), + }, + # Test case with a quartic Bézier + # to check if the fallback algorithms work + 4: { + 0: np.array( + [ + [1, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [0, 1, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1], + ] + ), + one_third: np.array( + [ + [81, 0, 0, 0, 0], + [54, 27, 0, 0, 0], + [36, 36, 9, 0, 0], + [24, 36, 18, 3, 0], + [16, 32, 24, 8, 1], + [16, 32, 24, 8, 1], + [0, 24, 36, 18, 3], + [0, 0, 36, 36, 9], + [0, 0, 0, 54, 27], + [0, 0, 0, 0, 81], + ] + ) + / 81, + two_thirds: np.array( + [ + [81, 0, 0, 0, 0], + [27, 54, 0, 0, 0], + [9, 36, 36, 0, 0], + [3, 18, 36, 24, 0], + [1, 8, 24, 32, 16], + [1, 8, 24, 32, 16], + [0, 3, 18, 36, 24], + [0, 0, 9, 36, 36], + [0, 0, 0, 27, 54], + [0, 0, 0, 0, 81], + ] + ) + / 81, + 1: np.array( + [ + [1, 0, 0, 0, 0], + [0, 1, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1], + [0, 0, 0, 0, 1], + [0, 0, 0, 0, 1], + [0, 0, 0, 0, 1], + [0, 0, 0, 0, 1], + [0, 0, 0, 0, 1], + ] + ), + }, +} diff --git a/tests/module/utils/_subdivision_matrices.py b/tests/module/utils/_subdivision_matrices.py new file mode 100644 index 0000000000..c020937dea --- /dev/null +++ b/tests/module/utils/_subdivision_matrices.py @@ -0,0 +1,167 @@ +import numpy as np + +# Expected values for matrices in subdivide_bezier and others +# Note that in bezier.py this is a list of dicts, +# not a dict of dicts! +SUBDIVISION_MATRICES = { + # For 0-degree Béziers + 0: { + 2: np.array([[1], [1]]), + 3: np.array([[1], [1], [1]]), + 4: np.array([[1], [1], [1], [1]]), + }, + # For linear Béziers + 1: { + 2: np.array( + [ + [2, 0], + [1, 1], + [1, 1], + [0, 2], + ] + ) + / 2, + 3: np.array( + [ + [3, 0], + [2, 1], + [2, 1], + [1, 2], + [1, 2], + [0, 3], + ] + ) + / 3, + 4: np.array( + [ + [4, 0], + [3, 1], + [3, 1], + [2, 2], + [2, 2], + [1, 3], + [1, 3], + [0, 4], + ] + ) + / 4, + }, + # For quadratic Béziers + 2: { + 2: np.array( + [ + [4, 0, 0], + [2, 2, 0], + [1, 2, 1], + [1, 2, 1], + [0, 2, 2], + [0, 0, 4], + ] + ) + / 4, + 3: np.array( + [ + [9, 0, 0], + [6, 3, 0], + [4, 4, 1], + [4, 4, 1], + [2, 5, 2], + [1, 4, 4], + [1, 4, 4], + [0, 3, 6], + [0, 0, 9], + ] + ) + / 9, + 4: np.array( + [ + [16, 0, 0], + [12, 4, 0], + [9, 6, 1], + [9, 6, 1], + [6, 8, 2], + [4, 8, 4], + [4, 8, 4], + [2, 8, 6], + [1, 6, 9], + [1, 6, 9], + [0, 4, 12], + [0, 0, 16], + ] + ) + / 16, + }, + # For cubic Béziers + 3: { + 2: np.array( + [ + [8, 0, 0, 0], + [4, 4, 0, 0], + [2, 4, 2, 0], + [1, 3, 3, 1], + [1, 3, 3, 1], + [0, 2, 4, 2], + [0, 0, 4, 4], + [0, 0, 0, 8], + ] + ) + / 8, + 3: np.array( + [ + [27, 0, 0, 0], + [18, 9, 0, 0], + [12, 12, 3, 0], + [8, 12, 6, 1], + [8, 12, 6, 1], + [4, 12, 9, 2], + [2, 9, 12, 4], + [1, 6, 12, 8], + [1, 6, 12, 8], + [0, 3, 12, 12], + [0, 0, 9, 18], + [0, 0, 0, 27], + ] + ) + / 27, + 4: np.array( + [ + [64, 0, 0, 0], + [48, 16, 0, 0], + [36, 24, 4, 0], + [27, 27, 9, 1], + [27, 27, 9, 1], + [18, 30, 14, 2], + [12, 28, 20, 4], + [8, 24, 24, 8], + [8, 24, 24, 8], + [4, 20, 28, 12], + [2, 14, 30, 18], + [1, 9, 27, 27], + [1, 9, 27, 27], + [0, 4, 24, 36], + [0, 0, 16, 48], + [0, 0, 0, 64], + ] + ) + / 64, + }, + # Test case with a quartic Bézier + # to check if the fallback algorithms work + 4: { + 2: np.array( + [ + [16, 0, 0, 0, 0], + [8, 8, 0, 0, 0], + [4, 8, 4, 0, 0], + [2, 6, 6, 2, 0], + [1, 4, 6, 4, 1], + [1, 4, 6, 4, 1], + [0, 2, 6, 6, 2], + [0, 0, 4, 8, 4], + [0, 0, 0, 8, 8], + [0, 0, 0, 0, 16], + ] + ) + / 16, + }, +} diff --git a/tests/module/utils/test_bezier.py b/tests/module/utils/test_bezier.py new file mode 100644 index 0000000000..7e1351c961 --- /dev/null +++ b/tests/module/utils/test_bezier.py @@ -0,0 +1,97 @@ +from __future__ import annotations + +import numpy as np +import numpy.testing as nt +from _split_matrices import SPLIT_MATRICES +from _subdivision_matrices import SUBDIVISION_MATRICES + +from manim.utils.bezier import ( + _get_subdivision_matrix, + partial_bezier_points, + split_bezier, + subdivide_bezier, +) + +QUARTIC_BEZIER = np.array( + [ + [-1, -1, 0], + [-1, 0, 0], + [0, 1, 0], + [1, 0, 0], + [1, -1, 0], + ], + dtype=float, +) + + +def test_partial_bezier_points() -> None: + """Test that :func:`partial_bezierpoints`, both in the + portion-matrix-building algorithm (degrees up to 3) and the + fallback algorithm (degree 4), works correctly. + """ + for degree, degree_dict in SUBDIVISION_MATRICES.items(): + n_points = degree + 1 + points = QUARTIC_BEZIER[:n_points] + for n_divisions, subdivision_matrix in degree_dict.items(): + for i in range(n_divisions): + a = i / n_divisions + b = (i + 1) / n_divisions + portion_matrix = subdivision_matrix[n_points * i : n_points * (i + 1)] + nt.assert_allclose( + partial_bezier_points(points, a, b), + portion_matrix @ points, + atol=1e-15, # Needed because of floating-point errors + ) + + +def test_split_bezier() -> None: + """Test that :func:`split_bezier`, both in the + split-matrix-building algorithm (degrees up to 3) and the + fallback algorithm (degree 4), works correctly. + """ + for degree, degree_dict in SPLIT_MATRICES.items(): + n_points = degree + 1 + points = QUARTIC_BEZIER[:n_points] + for t, split_matrix in degree_dict.items(): + nt.assert_allclose( + split_bezier(points, t), split_matrix @ points, atol=1e-15 + ) + + for degree, degree_dict in SUBDIVISION_MATRICES.items(): + n_points = degree + 1 + points = QUARTIC_BEZIER[:n_points] + # Split in half + split_matrix = degree_dict[2] + nt.assert_allclose( + split_bezier(points, 0.5), + split_matrix @ points, + ) + + +def test_get_subdivision_matrix() -> None: + """Test that the memos in .:meth:`_get_subdivision_matrix` + are being correctly generated. + """ + # Only for degrees up to 3! + for degree in range(4): + degree_dict = SUBDIVISION_MATRICES[degree] + for n_divisions, subdivision_matrix in degree_dict.items(): + nt.assert_allclose( + _get_subdivision_matrix(degree + 1, n_divisions), + subdivision_matrix, + ) + + +def test_subdivide_bezier() -> None: + """Test that :func:`subdivide_bezier`, both in the memoized cases + (degrees up to 3) and the fallback algorithm (degree 4), works + correctly. + """ + for degree, degree_dict in SUBDIVISION_MATRICES.items(): + n_points = degree + 1 + points = QUARTIC_BEZIER[:n_points] + for n_divisions, subdivision_matrix in degree_dict.items(): + nt.assert_allclose( + subdivide_bezier(points, n_divisions), + subdivision_matrix @ points, + ) From 739958514f7d3bb680651c66e25a68fbdf4f40f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 May 2024 09:00:12 -0400 Subject: [PATCH 3/8] Bump requests to 2.32.0 (#3776) updated-dependencies: - dependency-name: requests dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 235 ++++------------------------------------------------ 1 file changed, 15 insertions(+), 220 deletions(-) diff --git a/poetry.lock b/poetry.lock index 16a5d81dbe..557074cb1e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "alabaster" version = "0.7.16" description = "A light, configurable Sphinx theme" -category = "dev" optional = false python-versions = ">=3.9" files = [ @@ -16,7 +15,6 @@ files = [ name = "anyio" version = "4.3.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -39,7 +37,6 @@ trio = ["trio (>=0.23)"] name = "appnope" version = "0.1.4" description = "Disable App Nap on macOS >= 10.9" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -51,7 +48,6 @@ files = [ name = "argon2-cffi" version = "23.1.0" description = "Argon2 for Python" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -72,7 +68,6 @@ typing = ["mypy"] name = "argon2-cffi-bindings" version = "21.2.0" description = "Low-level CFFI bindings for Argon2" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -110,7 +105,6 @@ tests = ["pytest"] name = "arrow" version = "1.3.0" description = "Better dates & times for Python" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -124,13 +118,12 @@ types-python-dateutil = ">=2.8.10" [package.extras] doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] -test = ["dateparser (>=1.0.0,<2.0.0)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (>=3.0.0,<4.0.0)"] +test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"] [[package]] name = "astor" version = "0.8.1" description = "Read/rewrite/write Python ASTs" -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" files = [ @@ -142,7 +135,6 @@ files = [ name = "asttokens" version = "2.4.1" description = "Annotate AST trees with source code positions" -category = "main" optional = true python-versions = "*" files = [ @@ -161,7 +153,6 @@ test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] name = "async-lru" version = "2.0.4" description = "Simple LRU cache for asyncio" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -176,7 +167,6 @@ typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} name = "attrs" version = "23.2.0" description = "Classes Without Boilerplate" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -196,7 +186,6 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p name = "av" version = "12.0.0" description = "Pythonic bindings for FFmpeg's libraries." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -251,7 +240,6 @@ files = [ name = "babel" version = "2.15.0" description = "Internationalization utilities" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -266,7 +254,6 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] name = "beautifulsoup4" version = "4.12.3" description = "Screen-scraping library" -category = "main" optional = false python-versions = ">=3.6.0" files = [ @@ -288,7 +275,6 @@ lxml = ["lxml"] name = "black" version = "24.4.2" description = "The uncompromising code formatter." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -335,7 +321,6 @@ uvloop = ["uvloop (>=0.15.2)"] name = "bleach" version = "6.1.0" description = "An easy safelist-based HTML-sanitizing tool." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -354,7 +339,6 @@ css = ["tinycss2 (>=1.1.0,<1.3)"] name = "certifi" version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -366,7 +350,6 @@ files = [ name = "cffi" version = "1.16.0" description = "Foreign Function Interface for Python calling C code." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -431,7 +414,6 @@ pycparser = "*" name = "cfgv" version = "3.4.0" description = "Validate configuration and produce human readable error messages." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -443,7 +425,6 @@ files = [ name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -543,7 +524,6 @@ files = [ name = "click" version = "8.1.7" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -558,7 +538,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "cloup" version = "3.0.5" description = "Adds features to Click: option groups, constraints, subcommand sections and help themes." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -573,7 +552,6 @@ click = ">=8.0,<9.0" name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -585,7 +563,6 @@ files = [ name = "comm" version = "0.2.2" description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -603,7 +580,6 @@ test = ["pytest"] name = "contourpy" version = "1.2.1" description = "Python library for calculating contours of 2D quadrilateral grids" -category = "dev" optional = false python-versions = ">=3.9" files = [ @@ -667,7 +643,6 @@ test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] name = "coverage" version = "7.5.1" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -735,7 +710,6 @@ toml = ["tomli"] name = "cryptography" version = "42.0.6" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -790,7 +764,6 @@ test-randomorder = ["pytest-randomly"] name = "cycler" version = "0.12.1" description = "Composable style cycles" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -806,7 +779,6 @@ tests = ["pytest", "pytest-cov", "pytest-xdist"] name = "cython" version = "3.0.10" description = "The Cython compiler for writing C extensions in the Python language." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" files = [ @@ -874,7 +846,6 @@ files = [ name = "dearpygui" version = "1.11.1" description = "DearPyGui: A simple Python GUI Toolkit" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -902,7 +873,6 @@ files = [ name = "debugpy" version = "1.8.1" description = "An implementation of the Debug Adapter Protocol for Python" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -934,7 +904,6 @@ files = [ name = "decorator" version = "5.1.1" description = "Decorators for Humans" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -946,7 +915,6 @@ files = [ name = "defusedxml" version = "0.7.1" description = "XML bomb protection for Python stdlib modules" -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -958,7 +926,6 @@ files = [ name = "deprecated" version = "1.2.14" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -976,7 +943,6 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] name = "distlib" version = "0.3.8" description = "Distribution utilities" -category = "dev" optional = false python-versions = "*" files = [ @@ -988,7 +954,6 @@ files = [ name = "docutils" version = "0.20.1" description = "Docutils -- Python Documentation Utilities" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1000,7 +965,6 @@ files = [ name = "exceptiongroup" version = "1.2.1" description = "Backport of PEP 654 (exception groups)" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1015,7 +979,6 @@ test = ["pytest (>=6)"] name = "execnet" version = "2.1.1" description = "execnet: rapid multi-Python deployment" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1030,7 +993,6 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] name = "executing" version = "2.0.1" description = "Get the currently executing AST node of a frame, and other information" -category = "main" optional = true python-versions = ">=3.5" files = [ @@ -1045,7 +1007,6 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth name = "fastjsonschema" version = "2.19.1" description = "Fastest Python implementation of JSON schema" -category = "main" optional = true python-versions = "*" files = [ @@ -1060,7 +1021,6 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc name = "filelock" version = "3.14.0" description = "A platform independent file lock." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1077,7 +1037,6 @@ typing = ["typing-extensions (>=4.8)"] name = "flake8" version = "6.1.0" description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" optional = false python-versions = ">=3.8.1" files = [ @@ -1094,7 +1053,6 @@ pyflakes = ">=3.1.0,<3.2.0" name = "flake8-bugbear" version = "23.12.2" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -category = "dev" optional = false python-versions = ">=3.8.1" files = [ @@ -1113,7 +1071,6 @@ dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", name = "flake8-builtins" version = "2.5.0" description = "Check for python builtins being used as variables or parameters" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1131,7 +1088,6 @@ test = ["pytest"] name = "flake8-comprehensions" version = "3.14.0" description = "A flake8 plugin to help you write better list/set/dict comprehensions." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1146,7 +1102,6 @@ flake8 = ">=3.0,<3.2.0 || >3.2.0" name = "flake8-docstrings" version = "1.7.0" description = "Extension for flake8 which uses pydocstyle to check docstrings" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1162,7 +1117,6 @@ pydocstyle = ">=2.1" name = "flake8-plugin-utils" version = "1.3.3" description = "The package provides base classes and utils for flake8 plugin writing" -category = "dev" optional = false python-versions = ">=3.6,<4.0" files = [ @@ -1174,7 +1128,6 @@ files = [ name = "flake8-pytest-style" version = "1.7.2" description = "A flake8 plugin checking common style issues or inconsistencies with pytest-based tests." -category = "dev" optional = false python-versions = ">=3.7.2,<4.0.0" files = [ @@ -1189,7 +1142,6 @@ flake8-plugin-utils = ">=1.3.2,<2.0.0" name = "flake8-rst-docstrings" version = "0.3.0" description = "Python docstring reStructuredText (RST) validator for flake8" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1209,7 +1161,6 @@ develop = ["build", "twine"] name = "flake8-simplify" version = "0.14.6" description = "flake8 plugin which checks for code that can be simplified" -category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -1225,7 +1176,6 @@ flake8 = ">=3.7" name = "fonttools" version = "4.51.0" description = "Tools to manipulate font files" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1291,7 +1241,6 @@ woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] name = "fqdn" version = "1.5.1" description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" -category = "main" optional = true python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" files = [ @@ -1303,7 +1252,6 @@ files = [ name = "furo" version = "2023.9.10" description = "A clean customisable Sphinx documentation theme." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1321,7 +1269,6 @@ sphinx-basic-ng = "*" name = "gitdb" version = "4.0.11" description = "Git Object Database" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1336,7 +1283,6 @@ smmap = ">=3.0.1,<6" name = "gitpython" version = "3.1.43" description = "GitPython is a Python library used to interact with Git repositories" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1355,7 +1301,6 @@ test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", name = "glcontext" version = "2.5.0" description = "Portable OpenGL Context" -category = "main" optional = false python-versions = "*" files = [ @@ -1431,7 +1376,6 @@ files = [ name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1443,7 +1387,6 @@ files = [ name = "httpcore" version = "1.0.5" description = "A minimal low-level HTTP client." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1458,14 +1401,13 @@ h11 = ">=0.13,<0.15" [package.extras] asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] trio = ["trio (>=0.22.0,<0.26.0)"] [[package]] name = "httpx" version = "0.27.0" description = "The next generation HTTP client." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1476,21 +1418,20 @@ files = [ [package.dependencies] anyio = "*" certifi = "*" -httpcore = ">=1.0.0,<2.0.0" +httpcore = "==1.*" idna = "*" sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "identify" version = "2.5.36" description = "File identification library for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1505,7 +1446,6 @@ license = ["ukkonen"] name = "idna" version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1517,7 +1457,6 @@ files = [ name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1529,7 +1468,6 @@ files = [ name = "importlib-metadata" version = "7.1.0" description = "Read metadata from Python packages" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1549,7 +1487,6 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", name = "importlib-resources" version = "6.4.0" description = "Read resources from Python packages" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1568,7 +1505,6 @@ testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "p name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1580,7 +1516,6 @@ files = [ name = "ipykernel" version = "6.29.4" description = "IPython Kernel for Jupyter" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1594,7 +1529,7 @@ comm = ">=0.1.1" debugpy = ">=1.6.5" ipython = ">=7.23.1" jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" matplotlib-inline = ">=0.1" nest-asyncio = "*" packaging = "*" @@ -1614,7 +1549,6 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio name = "ipython" version = "8.18.1" description = "IPython: Productive Interactive Computing" -category = "main" optional = true python-versions = ">=3.9" files = [ @@ -1652,7 +1586,6 @@ test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pa name = "isoduration" version = "20.11.0" description = "Operations with ISO 8601 durations" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1667,7 +1600,6 @@ arrow = ">=0.15.0" name = "isort" version = "5.13.2" description = "A Python utility / library to sort Python imports." -category = "dev" optional = false python-versions = ">=3.8.0" files = [ @@ -1682,7 +1614,6 @@ colors = ["colorama (>=0.4.6)"] name = "isosurfaces" version = "0.1.2" description = "Construct isolines/isosurfaces over a 2D/3D scalar field defined by a function (not a uniform grid)" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1697,7 +1628,6 @@ numpy = "*" name = "jedi" version = "0.19.1" description = "An autocompletion tool for Python that can be used for text editors." -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -1717,7 +1647,6 @@ testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] name = "jinja2" version = "3.1.4" description = "A very fast and expressive template engine." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1735,7 +1664,6 @@ i18n = ["Babel (>=2.7)"] name = "json5" version = "0.9.25" description = "A Python implementation of the JSON5 data format." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1747,7 +1675,6 @@ files = [ name = "jsonpointer" version = "2.4" description = "Identify specific nodes in a JSON document (RFC 6901)" -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" files = [ @@ -1759,7 +1686,6 @@ files = [ name = "jsonschema" version = "4.22.0" description = "An implementation of JSON Schema validation for Python" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1789,7 +1715,6 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "jsonschema-specifications" version = "2023.12.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1804,7 +1729,6 @@ referencing = ">=0.31.0" name = "jupyter-client" version = "8.6.1" description = "Jupyter protocol implementation and client libraries" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1814,7 +1738,7 @@ files = [ [package.dependencies] importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} -jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" python-dateutil = ">=2.8.2" pyzmq = ">=23.0" tornado = ">=6.2" @@ -1828,7 +1752,6 @@ test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pyt name = "jupyter-core" version = "5.7.2" description = "Jupyter core package. A base package on which Jupyter projects rely." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1849,7 +1772,6 @@ test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout" name = "jupyter-events" version = "0.10.0" description = "Jupyter Event System library" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1875,7 +1797,6 @@ test = ["click", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "p name = "jupyter-lsp" version = "2.2.5" description = "Multi-Language Server WebSocket proxy for Jupyter Notebook/Lab server" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1891,7 +1812,6 @@ jupyter-server = ">=1.1.2" name = "jupyter-server" version = "2.14.0" description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1904,7 +1824,7 @@ anyio = ">=3.1.0" argon2-cffi = ">=21.1" jinja2 = ">=3.0.3" jupyter-client = ">=7.4.4" -jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" jupyter-events = ">=0.9.0" jupyter-server-terminals = ">=0.4.4" nbconvert = ">=6.4.4" @@ -1928,7 +1848,6 @@ test = ["flaky", "ipykernel", "pre-commit", "pytest (>=7.0,<9)", "pytest-console name = "jupyter-server-terminals" version = "0.5.3" description = "A Jupyter Server Extension Providing Terminals." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1948,7 +1867,6 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> name = "jupyterlab" version = "4.1.8" description = "JupyterLab computational environment" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1983,7 +1901,6 @@ upgrade-extension = ["copier (>=8.0,<9.0)", "jinja2-time (<0.3)", "pydantic (<2. name = "jupyterlab-pygments" version = "0.3.0" description = "Pygments theme using JupyterLab CSS variables" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1995,7 +1912,6 @@ files = [ name = "jupyterlab-server" version = "2.27.1" description = "A set of server components for JupyterLab and JupyterLab like applications." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -2022,7 +1938,6 @@ test = ["hatch", "ipykernel", "openapi-core (>=0.18.0,<0.19.0)", "openapi-spec-v name = "kiwisolver" version = "1.4.5" description = "A fast implementation of the Cassowary constraint solver" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2136,7 +2051,6 @@ files = [ name = "manimpango" version = "0.5.0" description = "Bindings for Pango for using with Manim." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2162,7 +2076,6 @@ files = [ name = "mapbox-earcut" version = "1.0.1" description = "Python bindings for the mapbox earcut C++ polygon triangulation library." -category = "main" optional = false python-versions = "*" files = [ @@ -2245,7 +2158,6 @@ test = ["pytest"] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2270,7 +2182,6 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "markupsafe" version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2340,7 +2251,6 @@ files = [ name = "matplotlib" version = "3.8.4" description = "Python plotting package" -category = "dev" optional = false python-versions = ">=3.9" files = [ @@ -2390,7 +2300,6 @@ python-dateutil = ">=2.7" name = "matplotlib-inline" version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -2405,7 +2314,6 @@ traitlets = "*" name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -2417,7 +2325,6 @@ files = [ name = "mdit-py-plugins" version = "0.4.0" description = "Collection of plugins for markdown-it-py" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2437,7 +2344,6 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2449,7 +2355,6 @@ files = [ name = "mistune" version = "3.0.2" description = "A sane and fast Markdown parser with useful plugins and renderers" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -2461,7 +2366,6 @@ files = [ name = "moderngl" version = "5.10.0" description = "ModernGL: High performance rendering for Python 3" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2538,7 +2442,6 @@ glcontext = ">=2.5.0,<3" name = "moderngl-window" version = "2.4.6" description = "A cross platform helper library for ModernGL making window creation and resource loading simple" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2570,7 +2473,6 @@ trimesh = ["trimesh (>=3.2.6,<4)"] name = "multipledispatch" version = "1.0.0" description = "Multiple dispatch" -category = "main" optional = false python-versions = "*" files = [ @@ -2582,7 +2484,6 @@ files = [ name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -2594,7 +2495,6 @@ files = [ name = "myst-parser" version = "2.0.0" description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2621,7 +2521,6 @@ testing-docutils = ["pygments", "pytest (>=7,<8)", "pytest-param-files (>=0.3.4, name = "nbclient" version = "0.10.0" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." -category = "main" optional = true python-versions = ">=3.8.0" files = [ @@ -2631,7 +2530,7 @@ files = [ [package.dependencies] jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" nbformat = ">=5.1" traitlets = ">=5.4" @@ -2644,7 +2543,6 @@ test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>= name = "nbconvert" version = "7.16.4" description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -2683,7 +2581,6 @@ webpdf = ["playwright"] name = "nbformat" version = "5.10.4" description = "The Jupyter Notebook format" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -2694,7 +2591,7 @@ files = [ [package.dependencies] fastjsonschema = ">=2.15" jsonschema = ">=2.6" -jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" traitlets = ">=5.1" [package.extras] @@ -2705,7 +2602,6 @@ test = ["pep440", "pre-commit", "pytest", "testpath"] name = "nest-asyncio" version = "1.6.0" description = "Patch asyncio to allow nested event loops" -category = "main" optional = true python-versions = ">=3.5" files = [ @@ -2717,7 +2613,6 @@ files = [ name = "networkx" version = "3.2.1" description = "Python package for creating and manipulating graphs and networks" -category = "main" optional = false python-versions = ">=3.9" files = [ @@ -2736,7 +2631,6 @@ test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] name = "nodeenv" version = "1.8.0" description = "Node.js virtual environment builder" -category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ @@ -2751,7 +2645,6 @@ setuptools = "*" name = "notebook" version = "7.1.3" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -2775,7 +2668,6 @@ test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4 name = "notebook-shim" version = "0.2.4" description = "A shim layer for notebook traits and config" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -2793,7 +2685,6 @@ test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync" name = "numpy" version = "1.26.4" description = "Fundamental package for array computing in Python" -category = "main" optional = false python-versions = ">=3.9" files = [ @@ -2839,7 +2730,6 @@ files = [ name = "overrides" version = "7.7.0" description = "A decorator to automatically detect mismatch when overriding a method." -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -2851,7 +2741,6 @@ files = [ name = "packaging" version = "24.0" description = "Core utilities for Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2863,7 +2752,6 @@ files = [ name = "pandocfilters" version = "1.5.1" description = "Utilities for writing pandoc filters in python" -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -2875,7 +2763,6 @@ files = [ name = "parso" version = "0.8.4" description = "A Python Parser" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -2891,7 +2778,6 @@ testing = ["docopt", "pytest"] name = "pathspec" version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2903,7 +2789,6 @@ files = [ name = "pexpect" version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." -category = "main" optional = true python-versions = "*" files = [ @@ -2918,7 +2803,6 @@ ptyprocess = ">=0.5" name = "pillow" version = "10.3.0" description = "Python Imaging Library (Fork)" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3005,7 +2889,6 @@ xmp = ["defusedxml"] name = "platformdirs" version = "4.2.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3022,7 +2905,6 @@ type = ["mypy (>=1.8)"] name = "pluggy" version = "1.5.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3038,7 +2920,6 @@ testing = ["pytest", "pytest-benchmark"] name = "pre-commit" version = "3.7.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" optional = false python-versions = ">=3.9" files = [ @@ -3057,7 +2938,6 @@ virtualenv = ">=20.10.0" name = "prometheus-client" version = "0.20.0" description = "Python client for the Prometheus monitoring system." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -3072,7 +2952,6 @@ twisted = ["twisted"] name = "prompt-toolkit" version = "3.0.43" description = "Library for building powerful interactive command lines in Python" -category = "main" optional = true python-versions = ">=3.7.0" files = [ @@ -3087,7 +2966,6 @@ wcwidth = "*" name = "psutil" version = "5.9.8" description = "Cross-platform lib for process and system monitoring in Python." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -3116,7 +2994,6 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] name = "psutil-wheels" version = "5.8.0" description = "Cross-platform lib for process and system monitoring in Python." -category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3142,7 +3019,6 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] name = "ptyprocess" version = "0.7.0" description = "Run a subprocess in a pseudo terminal" -category = "main" optional = true python-versions = "*" files = [ @@ -3154,7 +3030,6 @@ files = [ name = "pure-eval" version = "0.2.2" description = "Safely evaluate AST nodes without side effects" -category = "main" optional = true python-versions = "*" files = [ @@ -3169,7 +3044,6 @@ tests = ["pytest"] name = "py" version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -3181,7 +3055,6 @@ files = [ name = "pycairo" version = "1.26.0" description = "Python interface for cairo" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3206,7 +3079,6 @@ files = [ name = "pycodestyle" version = "2.11.1" description = "Python style guide checker" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3218,7 +3090,6 @@ files = [ name = "pycparser" version = "2.22" description = "C parser in Python" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3230,7 +3101,6 @@ files = [ name = "pydocstyle" version = "6.3.0" description = "Python docstring style checker" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3248,7 +3118,6 @@ toml = ["tomli (>=1.2.3)"] name = "pydub" version = "0.25.1" description = "Manipulate audio with an simple and easy high level interface" -category = "main" optional = false python-versions = "*" files = [ @@ -3260,7 +3129,6 @@ files = [ name = "pyflakes" version = "3.1.0" description = "passive checker of Python programs" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3272,7 +3140,6 @@ files = [ name = "pygithub" version = "2.3.0" description = "Use the full Github API v3" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3292,7 +3159,6 @@ urllib3 = ">=1.26.0" name = "pyglet" version = "2.0.15" description = "pyglet is a cross-platform games and multimedia package." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3304,7 +3170,6 @@ files = [ name = "pygments" version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3319,7 +3184,6 @@ windows-terminal = ["colorama (>=0.4.6)"] name = "pyjwt" version = "2.8.0" description = "JSON Web Token implementation in Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3340,7 +3204,6 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] name = "pynacl" version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3367,7 +3230,6 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] name = "pyobjc-core" version = "10.2" description = "Python<->ObjC Interoperability Module" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3384,7 +3246,6 @@ files = [ name = "pyobjc-framework-cocoa" version = "10.2" description = "Wrappers for the Cocoa frameworks on macOS" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3404,7 +3265,6 @@ pyobjc-core = ">=10.2" name = "pyparsing" version = "3.1.2" description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "dev" optional = false python-versions = ">=3.6.8" files = [ @@ -3419,7 +3279,6 @@ diagrams = ["jinja2", "railroad-diagrams"] name = "pyrr" version = "0.10.3" description = "3D mathematical functions using NumPy" -category = "main" optional = false python-versions = "*" files = [ @@ -3435,7 +3294,6 @@ numpy = "*" name = "pytest" version = "7.4.4" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3458,7 +3316,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3477,7 +3334,6 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale name = "pytest-forked" version = "1.6.0" description = "run tests in isolated forked subprocesses" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3493,7 +3349,6 @@ pytest = ">=3.10" name = "pytest-xdist" version = "2.5.0" description = "pytest xdist plugin for distributed testing and loop-on-failing modes" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3515,7 +3370,6 @@ testing = ["filelock"] name = "python-dateutil" version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -3530,7 +3384,6 @@ six = ">=1.5" name = "python-json-logger" version = "2.0.7" description = "A python library adding a json log formatter" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -3542,7 +3395,6 @@ files = [ name = "pywin32" version = "306" description = "Python for Window Extensions" -category = "main" optional = true python-versions = "*" files = [ @@ -3566,7 +3418,6 @@ files = [ name = "pywinpty" version = "2.0.13" description = "Pseudo terminal support for Windows from Python." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -3582,7 +3433,6 @@ files = [ name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3643,7 +3493,6 @@ files = [ name = "pyzmq" version = "26.0.3" description = "Python bindings for 0MQ" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -3744,7 +3593,6 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} name = "referencing" version = "0.35.1" description = "JSON Referencing + Python" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -3758,14 +3606,13 @@ rpds-py = ">=0.7.0" [[package]] name = "requests" -version = "2.31.0" +version = "2.32.0" description = "Python HTTP for Humans." -category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.0-py3-none-any.whl", hash = "sha256:f2c3881dddb70d056c5bd7600a4fae312b2a300e39be6a118d30b90bd27262b5"}, + {file = "requests-2.32.0.tar.gz", hash = "sha256:fa5490319474c82ef1d2c9bc459d3652e3ae4ef4c4ebdd18a21145a47ca4b6b8"}, ] [package.dependencies] @@ -3782,7 +3629,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "restructuredtext-lint" version = "1.4.0" description = "reStructuredText linter" -category = "dev" optional = false python-versions = "*" files = [ @@ -3796,7 +3642,6 @@ docutils = ">=0.11,<1.0" name = "rfc3339-validator" version = "0.1.4" description = "A pure python RFC3339 validator" -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -3811,7 +3656,6 @@ six = "*" name = "rfc3986-validator" version = "0.1.1" description = "Pure python rfc3986 validator" -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -3823,7 +3667,6 @@ files = [ name = "rich" version = "13.7.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -3842,7 +3685,6 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] name = "rpds-py" version = "0.18.0" description = "Python bindings to Rust's persistent data structures (rpds)" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -3951,7 +3793,6 @@ files = [ name = "scipy" version = "1.13.0" description = "Fundamental algorithms for scientific computing in Python" -category = "main" optional = false python-versions = ">=3.9" files = [ @@ -3994,7 +3835,6 @@ test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "po name = "screeninfo" version = "0.8.1" description = "Fetch location and size of physical screens." -category = "main" optional = false python-versions = ">=3.6.2,<4.0.0" files = [ @@ -4010,7 +3850,6 @@ pyobjc-framework-Cocoa = {version = "*", markers = "sys_platform == \"darwin\""} name = "send2trash" version = "1.8.3" description = "Send file to trash natively under Mac OS X, Windows and Linux" -category = "main" optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -4027,7 +3866,6 @@ win32 = ["pywin32"] name = "setuptools" version = "69.5.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4044,7 +3882,6 @@ testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jar name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -4056,7 +3893,6 @@ files = [ name = "skia-pathops" version = "0.8.0.post1" description = "Python access to operations on paths using the Skia library" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4108,7 +3944,6 @@ testing = ["coverage", "pytest", "pytest-randomly", "pytest-xdist"] name = "smmap" version = "5.0.1" description = "A pure Python implementation of a sliding window memory map manager" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4120,7 +3955,6 @@ files = [ name = "sniffio" version = "1.3.1" description = "Sniff out which async library your code is running under" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4132,7 +3966,6 @@ files = [ name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "dev" optional = false python-versions = "*" files = [ @@ -4144,7 +3977,6 @@ files = [ name = "soupsieve" version = "2.5" description = "A modern CSS selector implementation for Beautiful Soup." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4156,7 +3988,6 @@ files = [ name = "sphinx" version = "7.3.7" description = "Python documentation generator" -category = "dev" optional = false python-versions = ">=3.9" files = [ @@ -4193,7 +4024,6 @@ test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=6.0)", "setuptools name = "sphinx-basic-ng" version = "1.0.0b2" description = "A modern skeleton for Sphinx themes." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4211,7 +4041,6 @@ docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-ta name = "sphinx-copybutton" version = "0.5.2" description = "Add a copy button to each of your code cells." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4230,7 +4059,6 @@ rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] name = "sphinxcontrib-applehelp" version = "1.0.8" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" -category = "dev" optional = false python-versions = ">=3.9" files = [ @@ -4247,7 +4075,6 @@ test = ["pytest"] name = "sphinxcontrib-devhelp" version = "1.0.6" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" -category = "dev" optional = false python-versions = ">=3.9" files = [ @@ -4264,7 +4091,6 @@ test = ["pytest"] name = "sphinxcontrib-htmlhelp" version = "2.0.5" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -category = "dev" optional = false python-versions = ">=3.9" files = [ @@ -4281,7 +4107,6 @@ test = ["html5lib", "pytest"] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -4296,7 +4121,6 @@ test = ["flake8", "mypy", "pytest"] name = "sphinxcontrib-programoutput" version = "0.17" description = "Sphinx extension to include program output" -category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" files = [ @@ -4311,7 +4135,6 @@ Sphinx = ">=1.7.0" name = "sphinxcontrib-qthelp" version = "1.0.7" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" -category = "dev" optional = false python-versions = ">=3.9" files = [ @@ -4328,7 +4151,6 @@ test = ["pytest"] name = "sphinxcontrib-serializinghtml" version = "1.1.10" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" -category = "dev" optional = false python-versions = ">=3.9" files = [ @@ -4345,7 +4167,6 @@ test = ["pytest"] name = "sphinxext-opengraph" version = "0.9.1" description = "Sphinx Extension to enable OGP support" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4360,7 +4181,6 @@ sphinx = ">=4.0" name = "srt" version = "3.5.3" description = "A tiny library for parsing, modifying, and composing SRT files." -category = "main" optional = false python-versions = ">=2.7" files = [ @@ -4371,7 +4191,6 @@ files = [ name = "stack-data" version = "0.6.3" description = "Extract data from python stack frames and tracebacks for informative displays" -category = "main" optional = true python-versions = "*" files = [ @@ -4391,7 +4210,6 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] name = "svgelements" version = "1.9.6" description = "Svg Elements Parsing" -category = "main" optional = false python-versions = "*" files = [ @@ -4403,7 +4221,6 @@ files = [ name = "terminado" version = "0.18.1" description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -4425,7 +4242,6 @@ typing = ["mypy (>=1.6,<2.0)", "traitlets (>=5.11.1)"] name = "tinycss2" version = "1.3.0" description = "A tiny CSS parser" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -4444,7 +4260,6 @@ test = ["pytest", "ruff"] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4456,7 +4271,6 @@ files = [ name = "tornado" version = "6.4" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -category = "main" optional = true python-versions = ">= 3.8" files = [ @@ -4477,7 +4291,6 @@ files = [ name = "tqdm" version = "4.66.4" description = "Fast, Extensible Progress Meter" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4498,7 +4311,6 @@ telegram = ["requests"] name = "traitlets" version = "5.14.3" description = "Traitlets Python configuration system" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -4514,7 +4326,6 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, name = "types-decorator" version = "0.1.7" description = "Typing stubs for decorator" -category = "dev" optional = false python-versions = "*" files = [ @@ -4526,7 +4337,6 @@ files = [ name = "types-docutils" version = "0.21.0.20240423" description = "Typing stubs for docutils" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4538,7 +4348,6 @@ files = [ name = "types-pillow" version = "10.2.0.20240423" description = "Typing stubs for Pillow" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4550,7 +4359,6 @@ files = [ name = "types-pygments" version = "2.17.0.20240310" description = "Typing stubs for Pygments" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4566,7 +4374,6 @@ types-setuptools = "*" name = "types-python-dateutil" version = "2.9.0.20240316" description = "Typing stubs for python-dateutil" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -4578,7 +4385,6 @@ files = [ name = "types-setuptools" version = "69.5.0.20240423" description = "Typing stubs for setuptools" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4590,7 +4396,6 @@ files = [ name = "typing-extensions" version = "4.11.0" description = "Backported and Experimental Type Hints for Python 3.8+" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4602,7 +4407,6 @@ files = [ name = "uri-template" version = "1.3.0" description = "RFC 6570 URI Template Processor" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4617,7 +4421,6 @@ dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake name = "urllib3" version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4635,7 +4438,6 @@ zstd = ["zstandard (>=0.18.0)"] name = "virtualenv" version = "20.26.1" description = "Virtual Python Environment builder" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4656,7 +4458,6 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess name = "watchdog" version = "4.0.0" description = "Filesystem events monitoring" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4698,7 +4499,6 @@ watchmedo = ["PyYAML (>=3.10)"] name = "wcwidth" version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" -category = "main" optional = true python-versions = "*" files = [ @@ -4710,7 +4510,6 @@ files = [ name = "webcolors" version = "1.13" description = "A library for working with the color formats defined by HTML and CSS." -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4726,7 +4525,6 @@ tests = ["pytest", "pytest-cov"] name = "webencodings" version = "0.5.1" description = "Character encoding aliases for legacy web content" -category = "main" optional = true python-versions = "*" files = [ @@ -4738,7 +4536,6 @@ files = [ name = "websocket-client" version = "1.8.0" description = "WebSocket client for Python with low level API options" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -4755,7 +4552,6 @@ test = ["websockets"] name = "wrapt" version = "1.16.0" description = "Module for decorators, wrappers and monkey patching." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -4835,7 +4631,6 @@ files = [ name = "zipp" version = "3.18.1" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.8" files = [ From 0d67dcdc6eaf798da708a36a62d36bd42750678d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez=20Novoa?= <49853152+chopan050@users.noreply.github.com> Date: Tue, 21 May 2024 15:30:46 -0400 Subject: [PATCH 4/8] Fix assertions and improve error messages when adding submobjects (#3756) * Optimized AnimationGroup computation of start-end times with lag ratio * Added extra comment for init_run_time * Added full path to imports in composition.py * Optimized AnimationGroup.interpolate * Fixed final bugs * Removed accidental print * Final fix to AnimationGroup.interpolate * Fixed animations being skipped unintentionally * Fix and improve Mobject assertions when adding submobjects * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update examples in Mobject.add() and OpenGLMobject.add() docstrings * overriden -> overridden * Joined string in OpenGLMobject error message * Address requested changes * OpenGLVMObjects -> OpenGLVMobjects * Use tuplify in VGroup.__setitem__() --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- manim/mobject/mobject.py | 86 +++++++++--- manim/mobject/opengl/opengl_mobject.py | 83 ++++++++++-- .../opengl/opengl_vectorized_mobject.py | 14 +- manim/mobject/types/vectorized_mobject.py | 17 +-- tests/module/mobject/mobject/test_mobject.py | 19 ++- .../test_vectorized_mobject.py | 122 ++++++++++++++++-- tests/opengl/test_opengl_mobject.py | 23 +++- .../opengl/test_opengl_vectorized_mobject.py | 122 ++++++++++++++++-- 8 files changed, 404 insertions(+), 82 deletions(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 3369f3a241..0ae2a29daa 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -116,6 +116,63 @@ def __init__( self.generate_points() self.init_colors() + def _assert_valid_submobjects(self, submobjects: Iterable[Mobject]) -> Self: + """Check that all submobjects are actually instances of + :class:`Mobject`, and that none of them is ``self`` (a + :class:`Mobject` cannot contain itself). + + This is an auxiliary function called when adding Mobjects to the + :attr:`submobjects` list. + + This function is intended to be overridden by subclasses such as + :class:`VMobject`, which should assert that only other VMobjects + may be added into it. + + Parameters + ---------- + submobjects + The list containing values to validate. + + Returns + ------- + :class:`Mobject` + The Mobject itself. + + Raises + ------ + TypeError + If any of the values in `submobjects` is not a :class:`Mobject`. + ValueError + If there was an attempt to add a :class:`Mobject` as its own + submobject. + """ + return self._assert_valid_submobjects_internal(submobjects, Mobject) + + def _assert_valid_submobjects_internal( + self, submobjects: list[Mobject], mob_class: type[Mobject] + ) -> Self: + for i, submob in enumerate(submobjects): + if not isinstance(submob, mob_class): + error_message = ( + f"Only values of type {mob_class.__name__} can be added " + f"as submobjects of {type(self).__name__}, but the value " + f"{submob} (at index {i}) is of type " + f"{type(submob).__name__}." + ) + # Intended for subclasses such as VMobject, which + # cannot have regular Mobjects as submobjects + if isinstance(submob, Mobject): + error_message += ( + " You can try adding this value into a Group instead." + ) + raise TypeError(error_message) + if submob is self: + raise ValueError( + f"Cannot add {type(self).__name__} as a submobject of " + f"itself (at index {i})." + ) + return self + @classmethod def animation_override_for( cls, @@ -414,12 +471,19 @@ def add(self, *mobjects: Mobject) -> Self: >>> len(outer.submobjects) 1 + Only Mobjects can be added:: + + >>> outer.add(3) + Traceback (most recent call last): + ... + TypeError: Only values of type Mobject can be added as submobjects of Mobject, but the value 3 (at index 0) is of type int. + Adding an object to itself raises an error:: >>> outer.add(outer) Traceback (most recent call last): ... - ValueError: Mobject cannot contain self + ValueError: Cannot add Mobject as a submobject of itself (at index 0). A given mobject cannot be added as a submobject twice to some parent:: @@ -433,12 +497,7 @@ def add(self, *mobjects: Mobject) -> Self: [child] """ - for m in mobjects: - if not isinstance(m, Mobject): - raise TypeError("All submobjects must be of type Mobject") - if m is self: - raise ValueError("Mobject cannot contain self") - + self._assert_valid_submobjects(mobjects) unique_mobjects = remove_list_redundancies(mobjects) if len(mobjects) != len(unique_mobjects): logger.warning( @@ -464,10 +523,7 @@ def insert(self, index: int, mobject: Mobject) -> None: mobject The mobject to be inserted. """ - if not isinstance(mobject, Mobject): - raise TypeError("All submobjects must be of type Mobject") - if mobject is self: - raise ValueError("Mobject cannot contain self") + self._assert_valid_submobjects([mobject]) self.submobjects.insert(index, mobject) def __add__(self, mobject: Mobject): @@ -520,13 +576,7 @@ def add_to_back(self, *mobjects: Mobject) -> Self: :meth:`add` """ - if self in mobjects: - raise ValueError("A mobject shouldn't contain itself") - - for mobject in mobjects: - if not isinstance(mobject, Mobject): - raise TypeError("All submobjects must be of type Mobject") - + self._assert_valid_submobjects(mobjects) self.remove(*mobjects) # dict.fromkeys() removes duplicates while maintaining order self.submobjects = list(dict.fromkeys(mobjects)) + self.submobjects diff --git a/manim/mobject/opengl/opengl_mobject.py b/manim/mobject/opengl/opengl_mobject.py index 38c65cdc89..3bdbd7c0b5 100644 --- a/manim/mobject/opengl/opengl_mobject.py +++ b/manim/mobject/opengl/opengl_mobject.py @@ -165,6 +165,64 @@ def __init__( self.should_render = should_render + def _assert_valid_submobjects(self, submobjects: Iterable[OpenGLMobject]) -> Self: + """Check that all submobjects are actually instances of + :class:`OpenGLMobject`, and that none of them is + ``self`` (an :class:`OpenGLMobject` cannot contain itself). + + This is an auxiliary function called when adding OpenGLMobjects to the + :attr:`submobjects` list. + + This function is intended to be overridden by subclasses such as + :class:`OpenGLVMobject`, which should assert that only other + OpenGLVMobjects may be added into it. + + Parameters + ---------- + submobjects + The list containing values to validate. + + Returns + ------- + :class:`OpenGLMobject` + The OpenGLMobject itself. + + Raises + ------ + TypeError + If any of the values in `submobjects` is not an + :class:`OpenGLMobject`. + ValueError + If there was an attempt to add an :class:`OpenGLMobject` as its own + submobject. + """ + return self._assert_valid_submobjects_internal(submobjects, OpenGLMobject) + + def _assert_valid_submobjects_internal( + self, submobjects: list[OpenGLMobject], mob_class: type[OpenGLMobject] + ) -> Self: + for i, submob in enumerate(submobjects): + if not isinstance(submob, mob_class): + error_message = ( + f"Only values of type {mob_class.__name__} can be added " + f"as submobjects of {type(self).__name__}, but the value " + f"{submob} (at index {i}) is of type " + f"{type(submob).__name__}." + ) + # Intended for subclasses such as OpenGLVMobject, which + # cannot have regular OpenGLMobjects as submobjects + if isinstance(submob, OpenGLMobject): + error_message += ( + " You can try adding this value into a Group instead." + ) + raise TypeError(error_message) + if submob is self: + raise ValueError( + f"Cannot add {type(self).__name__} as a submobject of " + f"itself (at index {i})." + ) + return self + @classmethod def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) @@ -734,28 +792,33 @@ def add( >>> len(outer.submobjects) 1 + Only OpenGLMobjects can be added:: + + >>> outer.add(3) + Traceback (most recent call last): + ... + TypeError: Only values of type OpenGLMobject can be added as submobjects of OpenGLMobject, but the value 3 (at index 0) is of type int. + Adding an object to itself raises an error:: >>> outer.add(outer) Traceback (most recent call last): ... - ValueError: OpenGLMobject cannot contain self + ValueError: Cannot add OpenGLMobject as a submobject of itself (at index 0). """ if update_parent: assert len(mobjects) == 1, "Can't set multiple parents." mobjects[0].parent = self - if self in mobjects: - raise ValueError("OpenGLMobject cannot contain self") + self._assert_valid_submobjects(mobjects) + if any(mobjects.count(elem) > 1 for elem in mobjects): logger.warning( "Attempted adding some Mobject as a child more than once, " "this is not possible. Repetitions are ignored.", ) for mobject in mobjects: - if not isinstance(mobject, OpenGLMobject): - raise TypeError("All submobjects must be of type OpenGLMobject") if mobject not in self.submobjects: self.submobjects.append(mobject) if self not in mobject.parents: @@ -784,11 +847,7 @@ def insert(self, index: int, mobject: OpenGLMobject, update_parent: bool = False if update_parent: mobject.parent = self - if mobject is self: - raise ValueError("OpenGLMobject cannot contain self") - - if not isinstance(mobject, OpenGLMobject): - raise TypeError("All submobjects must be of type OpenGLMobject") + self._assert_valid_submobjects([mobject]) if mobject not in self.submobjects: self.submobjects.insert(index, mobject) @@ -880,10 +939,12 @@ def add_to_back(self, *mobjects: OpenGLMobject) -> OpenGLMobject: :meth:`add` """ + self._assert_valid_submobjects(mobjects) self.submobjects = list_update(mobjects, self.submobjects) return self def replace_submobject(self, index, new_submob): + self._assert_valid_submobjects([new_submob]) old_submob = self.submobjects[index] if self in old_submob.parents: old_submob.parents.remove(self) @@ -2734,8 +2795,6 @@ def throw_error_if_no_points(self): class OpenGLGroup(OpenGLMobject): def __init__(self, *mobjects, **kwargs): - if not all(isinstance(m, OpenGLMobject) for m in mobjects): - raise Exception("All submobjects must be of type OpenGLMobject") super().__init__(**kwargs) self.add(*mobjects) diff --git a/manim/mobject/opengl/opengl_vectorized_mobject.py b/manim/mobject/opengl/opengl_vectorized_mobject.py index 41d6aac084..b7b5d4ce54 100644 --- a/manim/mobject/opengl/opengl_vectorized_mobject.py +++ b/manim/mobject/opengl/opengl_vectorized_mobject.py @@ -25,7 +25,7 @@ ) from manim.utils.color import BLACK, WHITE, ManimColor, ParsableManimColor from manim.utils.config_ops import _Data -from manim.utils.iterables import listify, make_even, resize_with_interpolation +from manim.utils.iterables import make_even, resize_with_interpolation, tuplify from manim.utils.space_ops import ( angle_between_vectors, cross2d, @@ -161,6 +161,9 @@ def __init__( if stroke_color is not None: self.stroke_color = ManimColor.parse(stroke_color) + def _assert_valid_submobjects(self, submobjects: Iterable[OpenGLVMobject]) -> Self: + return self._assert_valid_submobjects_internal(submobjects, OpenGLVMobject) + def get_group_class(self): return OpenGLVGroup @@ -266,7 +269,7 @@ def set_stroke( if width is not None: for mob in self.get_family(recurse): - mob.stroke_width = np.array([[width] for width in listify(width)]) + mob.stroke_width = np.array([[width] for width in tuplify(width)]) if background is not None: for mob in self.get_family(recurse): @@ -1659,8 +1662,6 @@ def construct(self): """ def __init__(self, *vmobjects, **kwargs): - if not all(isinstance(m, OpenGLVMobject) for m in vmobjects): - raise Exception("All submobjects must be of type OpenGLVMobject") super().__init__(**kwargs) self.add(*vmobjects) @@ -1726,8 +1727,6 @@ def construct(self): (gr-circle_red).animate.shift(RIGHT) ) """ - if not all(isinstance(m, OpenGLVMobject) for m in vmobjects): - raise TypeError("All submobjects must be of type OpenGLVMobject") return super().add(*vmobjects) def __add__(self, vmobject): @@ -1773,8 +1772,7 @@ def __setitem__(self, key: int, value: OpenGLVMobject | Sequence[OpenGLVMobject] >>> config.renderer = original_renderer """ - if not all(isinstance(m, OpenGLVMobject) for m in value): - raise TypeError("All submobjects must be of type OpenGLVMobject") + self._assert_valid_submobjects(tuplify(value)) self.submobjects[key] = value diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index deb3690620..d82346868a 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -172,6 +172,9 @@ def __init__( if stroke_color is not None: self.stroke_color = ManimColor.parse(stroke_color) + def _assert_valid_submobjects(self, submobjects: Iterable[VMobject]) -> Self: + return self._assert_valid_submobjects_internal(submobjects, VMobject) + # OpenGL compatibility @property def n_points_per_curve(self) -> int: @@ -2016,14 +2019,6 @@ def construct(self): (gr-circle_red).animate.shift(RIGHT) ) """ - for m in vmobjects: - if not isinstance(m, (VMobject, OpenGLVMobject)): - raise TypeError( - f"All submobjects of {self.__class__.__name__} must be of type VMobject. " - f"Got {repr(m)} ({type(m).__name__}) instead. " - "You can try using `Group` instead." - ) - return super().add(*vmobjects) def __add__(self, vmobject: VMobject) -> Self: @@ -2061,8 +2056,7 @@ def __setitem__(self, key: int, value: VMobject | Sequence[VMobject]) -> None: >>> new_obj = VMobject() >>> vgroup[0] = new_obj """ - if not all(isinstance(m, (VMobject, OpenGLVMobject)) for m in value): - raise TypeError("All submobjects must be of type VMobject") + self._assert_valid_submobjects(tuplify(value)) self.submobjects[key] = value @@ -2390,8 +2384,7 @@ def add_key_value_pair(self, key: Hashable, value: VMobject) -> None: self.add_key_value_pair('s', square_obj) """ - if not isinstance(value, (VMobject, OpenGLVMobject)): - raise TypeError("All submobjects must be of type VMobject") + self._assert_valid_submobjects([value]) mob = value if self.show_keys: # This import is here and not at the top to avoid circular import diff --git a/tests/module/mobject/mobject/test_mobject.py b/tests/module/mobject/mobject/test_mobject.py index bb228eb798..89deea93c9 100644 --- a/tests/module/mobject/mobject/test_mobject.py +++ b/tests/module/mobject/mobject/test_mobject.py @@ -26,15 +26,26 @@ def test_mobject_add(): # check that Mobject.add() returns the Mobject (for chained calls) assert obj.add(Mobject()) is obj + assert len(obj.submobjects) == 13 + obj = Mobject() # a Mobject cannot contain itself - with pytest.raises(ValueError): - obj.add(obj) + with pytest.raises(ValueError) as add_self_info: + obj.add(Mobject(), obj, Mobject()) + assert str(add_self_info.value) == ( + "Cannot add Mobject as a submobject of itself (at index 1)." + ) + assert len(obj.submobjects) == 0 # can only add Mobjects - with pytest.raises(TypeError): - obj.add("foo") + with pytest.raises(TypeError) as add_str_info: + obj.add(Mobject(), Mobject(), "foo") + assert str(add_str_info.value) == ( + "Only values of type Mobject can be added as submobjects of Mobject, " + "but the value foo (at index 2) is of type str." + ) + assert len(obj.submobjects) == 0 def test_mobject_remove(): diff --git a/tests/module/mobject/types/vectorized_mobject/test_vectorized_mobject.py b/tests/module/mobject/types/vectorized_mobject/test_vectorized_mobject.py index a17383fd98..7e92a68ac8 100644 --- a/tests/module/mobject/types/vectorized_mobject/test_vectorized_mobject.py +++ b/tests/module/mobject/types/vectorized_mobject/test_vectorized_mobject.py @@ -18,7 +18,54 @@ from manim.constants import PI -def test_vmobject_point_from_propotion(): +def test_vmobject_add(): + """Test the VMobject add method.""" + obj = VMobject() + assert len(obj.submobjects) == 0 + + obj.add(VMobject()) + assert len(obj.submobjects) == 1 + + # Can't add non-VMobject values to a VMobject. + with pytest.raises(TypeError) as add_int_info: + obj.add(3) + assert str(add_int_info.value) == ( + "Only values of type VMobject can be added as submobjects of VMobject, " + "but the value 3 (at index 0) is of type int." + ) + assert len(obj.submobjects) == 1 + + # Plain Mobjects can't be added to a VMobject if they're not + # VMobjects. Suggest adding them into a Group instead. + with pytest.raises(TypeError) as add_mob_info: + obj.add(Mobject()) + assert str(add_mob_info.value) == ( + "Only values of type VMobject can be added as submobjects of VMobject, " + "but the value Mobject (at index 0) is of type Mobject. You can try " + "adding this value into a Group instead." + ) + assert len(obj.submobjects) == 1 + + with pytest.raises(TypeError) as add_vmob_and_mob_info: + # If only one of the added objects is not an instance of VMobject, none of them should be added + obj.add(VMobject(), Mobject()) + assert str(add_vmob_and_mob_info.value) == ( + "Only values of type VMobject can be added as submobjects of VMobject, " + "but the value Mobject (at index 1) is of type Mobject. You can try " + "adding this value into a Group instead." + ) + assert len(obj.submobjects) == 1 + + # A VMobject or VGroup cannot contain itself. + with pytest.raises(ValueError) as add_self_info: + obj.add(obj) + assert str(add_self_info.value) == ( + "Cannot add VMobject as a submobject of itself (at index 0)." + ) + assert len(obj.submobjects) == 1 + + +def test_vmobject_point_from_proportion(): obj = VMobject() # One long line, one short line @@ -79,28 +126,77 @@ def test_vgroup_init(): VGroup() VGroup(VMobject()) VGroup(VMobject(), VMobject()) - with pytest.raises(TypeError): + + # A VGroup cannot contain non-VMobject values. + with pytest.raises(TypeError) as init_with_float_info: + VGroup(3.0) + assert str(init_with_float_info.value) == ( + "Only values of type VMobject can be added as submobjects of VGroup, " + "but the value 3.0 (at index 0) is of type float." + ) + + with pytest.raises(TypeError) as init_with_mob_info: VGroup(Mobject()) - with pytest.raises(TypeError): - VGroup(Mobject(), Mobject()) + assert str(init_with_mob_info.value) == ( + "Only values of type VMobject can be added as submobjects of VGroup, " + "but the value Mobject (at index 0) is of type Mobject. You can try " + "adding this value into a Group instead." + ) + + with pytest.raises(TypeError) as init_with_vmob_and_mob_info: + VGroup(VMobject(), Mobject()) + assert str(init_with_vmob_and_mob_info.value) == ( + "Only values of type VMobject can be added as submobjects of VGroup, " + "but the value Mobject (at index 1) is of type Mobject. You can try " + "adding this value into a Group instead." + ) def test_vgroup_add(): """Test the VGroup add method.""" obj = VGroup() assert len(obj.submobjects) == 0 + obj.add(VMobject()) assert len(obj.submobjects) == 1 - with pytest.raises(TypeError): + + # Can't add non-VMobject values to a VMobject or VGroup. + with pytest.raises(TypeError) as add_int_info: + obj.add(3) + assert str(add_int_info.value) == ( + "Only values of type VMobject can be added as submobjects of VGroup, " + "but the value 3 (at index 0) is of type int." + ) + assert len(obj.submobjects) == 1 + + # Plain Mobjects can't be added to a VMobject or VGroup if they're not + # VMobjects. Suggest adding them into a Group instead. + with pytest.raises(TypeError) as add_mob_info: obj.add(Mobject()) + assert str(add_mob_info.value) == ( + "Only values of type VMobject can be added as submobjects of VGroup, " + "but the value Mobject (at index 0) is of type Mobject. You can try " + "adding this value into a Group instead." + ) assert len(obj.submobjects) == 1 - with pytest.raises(TypeError): - # If only one of the added object is not an instance of VMobject, none of them should be added + + with pytest.raises(TypeError) as add_vmob_and_mob_info: + # If only one of the added objects is not an instance of VMobject, none of them should be added obj.add(VMobject(), Mobject()) + assert str(add_vmob_and_mob_info.value) == ( + "Only values of type VMobject can be added as submobjects of VGroup, " + "but the value Mobject (at index 1) is of type Mobject. You can try " + "adding this value into a Group instead." + ) assert len(obj.submobjects) == 1 - with pytest.raises(ValueError): - # a Mobject cannot contain itself + + # A VMobject or VGroup cannot contain itself. + with pytest.raises(ValueError) as add_self_info: obj.add(obj) + assert str(add_self_info.value) == ( + "Cannot add VGroup as a submobject of itself (at index 0)." + ) + assert len(obj.submobjects) == 1 def test_vgroup_add_dunder(): @@ -234,7 +330,7 @@ def test_vgroup_supports_item_assigment(): def test_vgroup_item_assignment_at_correct_position(): - """Test VGroup item-assignment adds to correct position for VMObjects""" + """Test VGroup item-assignment adds to correct position for VMobjects""" n_items = 10 vgroup = VGroup() for _i in range(n_items): @@ -248,8 +344,12 @@ def test_vgroup_item_assignment_at_correct_position(): def test_vgroup_item_assignment_only_allows_vmobjects(): """Test VGroup item-assignment raises TypeError when invalid type is passed""" vgroup = VGroup(VMobject()) - with pytest.raises(TypeError, match="All submobjects must be of type VMobject"): + with pytest.raises(TypeError) as assign_str_info: vgroup[0] = "invalid object" + assert str(assign_str_info.value) == ( + "Only values of type VMobject can be added as submobjects of VGroup, " + "but the value invalid object (at index 0) is of type str." + ) def test_trim_dummy(): diff --git a/tests/opengl/test_opengl_mobject.py b/tests/opengl/test_opengl_mobject.py index f1a58fa6a3..d5bfac97f9 100644 --- a/tests/opengl/test_opengl_mobject.py +++ b/tests/opengl/test_opengl_mobject.py @@ -25,15 +25,26 @@ def test_opengl_mobject_add(using_opengl_renderer): # check that OpenGLMobject.add() returns the OpenGLMobject (for chained calls) assert obj.add(OpenGLMobject()) is obj + assert len(obj.submobjects) == 13 + obj = OpenGLMobject() - # a OpenGLMobject cannot contain itself - with pytest.raises(ValueError): - obj.add(obj) + # an OpenGLMobject cannot contain itself + with pytest.raises(ValueError) as add_self_info: + obj.add(OpenGLMobject(), obj, OpenGLMobject()) + assert str(add_self_info.value) == ( + "Cannot add OpenGLMobject as a submobject of itself (at index 1)." + ) + assert len(obj.submobjects) == 0 - # can only add OpenGLMobjects - with pytest.raises(TypeError): - obj.add("foo") + # can only add Mobjects + with pytest.raises(TypeError) as add_str_info: + obj.add(OpenGLMobject(), OpenGLMobject(), "foo") + assert str(add_str_info.value) == ( + "Only values of type OpenGLMobject can be added as submobjects of " + "OpenGLMobject, but the value foo (at index 2) is of type str." + ) + assert len(obj.submobjects) == 0 def test_opengl_mobject_remove(using_opengl_renderer): diff --git a/tests/opengl/test_opengl_vectorized_mobject.py b/tests/opengl/test_opengl_vectorized_mobject.py index 8e0ba22582..6f73ef0265 100644 --- a/tests/opengl/test_opengl_vectorized_mobject.py +++ b/tests/opengl/test_opengl_vectorized_mobject.py @@ -8,7 +8,54 @@ from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject -def test_opengl_vmobject_point_from_propotion(using_opengl_renderer): +def test_opengl_vmobject_add(using_opengl_renderer): + """Test the OpenGLVMobject add method.""" + obj = OpenGLVMobject() + assert len(obj.submobjects) == 0 + + obj.add(OpenGLVMobject()) + assert len(obj.submobjects) == 1 + + # Can't add non-OpenGLVMobject values to a VMobject. + with pytest.raises(TypeError) as add_int_info: + obj.add(3) + assert str(add_int_info.value) == ( + "Only values of type OpenGLVMobject can be added as submobjects of " + "OpenGLVMobject, but the value 3 (at index 0) is of type int." + ) + assert len(obj.submobjects) == 1 + + # Plain OpenGLMobjects can't be added to a OpenGLVMobject if they're not + # OpenGLVMobjects. Suggest adding them into an OpenGLGroup instead. + with pytest.raises(TypeError) as add_mob_info: + obj.add(OpenGLMobject()) + assert str(add_mob_info.value) == ( + "Only values of type OpenGLVMobject can be added as submobjects of " + "OpenGLVMobject, but the value OpenGLMobject (at index 0) is of type " + "OpenGLMobject. You can try adding this value into a Group instead." + ) + assert len(obj.submobjects) == 1 + + with pytest.raises(TypeError) as add_vmob_and_mob_info: + # If only one of the added objects is not an instance of VMobject, none of them should be added + obj.add(OpenGLVMobject(), OpenGLMobject()) + assert str(add_vmob_and_mob_info.value) == ( + "Only values of type OpenGLVMobject can be added as submobjects of " + "OpenGLVMobject, but the value OpenGLMobject (at index 1) is of type " + "OpenGLMobject. You can try adding this value into a Group instead." + ) + assert len(obj.submobjects) == 1 + + # A VMobject or VGroup cannot contain itself. + with pytest.raises(ValueError) as add_self_info: + obj.add(obj) + assert str(add_self_info.value) == ( + "Cannot add OpenGLVMobject as a submobject of itself (at index 0)." + ) + assert len(obj.submobjects) == 1 + + +def test_opengl_vmobject_point_from_proportion(using_opengl_renderer): obj = OpenGLVMobject() # One long line, one short line @@ -37,28 +84,77 @@ def test_vgroup_init(using_opengl_renderer): VGroup() VGroup(OpenGLVMobject()) VGroup(OpenGLVMobject(), OpenGLVMobject()) - with pytest.raises(TypeError): + + # A VGroup cannot contain non-VMobject values. + with pytest.raises(TypeError) as init_with_float_info: + VGroup(3.0) + assert str(init_with_float_info.value) == ( + "Only values of type OpenGLVMobject can be added as submobjects of " + "VGroup, but the value 3.0 (at index 0) is of type float." + ) + + with pytest.raises(TypeError) as init_with_mob_info: VGroup(OpenGLMobject()) - with pytest.raises(TypeError): - VGroup(OpenGLMobject(), OpenGLMobject()) + assert str(init_with_mob_info.value) == ( + "Only values of type OpenGLVMobject can be added as submobjects of " + "VGroup, but the value OpenGLMobject (at index 0) is of type " + "OpenGLMobject. You can try adding this value into a Group instead." + ) + + with pytest.raises(TypeError) as init_with_vmob_and_mob_info: + VGroup(OpenGLVMobject(), OpenGLMobject()) + assert str(init_with_vmob_and_mob_info.value) == ( + "Only values of type OpenGLVMobject can be added as submobjects of " + "VGroup, but the value OpenGLMobject (at index 1) is of type " + "OpenGLMobject. You can try adding this value into a Group instead." + ) def test_vgroup_add(using_opengl_renderer): """Test the VGroup add method.""" obj = VGroup() assert len(obj.submobjects) == 0 + obj.add(OpenGLVMobject()) assert len(obj.submobjects) == 1 - with pytest.raises(TypeError): + + # Can't add non-OpenGLVMobject values to a VMobject. + with pytest.raises(TypeError) as add_int_info: + obj.add(3) + assert str(add_int_info.value) == ( + "Only values of type OpenGLVMobject can be added as submobjects of " + "VGroup, but the value 3 (at index 0) is of type int." + ) + assert len(obj.submobjects) == 1 + + # Plain OpenGLMobjects can't be added to a OpenGLVMobject if they're not + # OpenGLVMobjects. Suggest adding them into an OpenGLGroup instead. + with pytest.raises(TypeError) as add_mob_info: obj.add(OpenGLMobject()) + assert str(add_mob_info.value) == ( + "Only values of type OpenGLVMobject can be added as submobjects of " + "VGroup, but the value OpenGLMobject (at index 0) is of type " + "OpenGLMobject. You can try adding this value into a Group instead." + ) assert len(obj.submobjects) == 1 - with pytest.raises(TypeError): - # If only one of the added object is not an instance of OpenGLVMobject, none of them should be added + + with pytest.raises(TypeError) as add_vmob_and_mob_info: + # If only one of the added objects is not an instance of VMobject, none of them should be added obj.add(OpenGLVMobject(), OpenGLMobject()) + assert str(add_vmob_and_mob_info.value) == ( + "Only values of type OpenGLVMobject can be added as submobjects of " + "VGroup, but the value OpenGLMobject (at index 1) is of type " + "OpenGLMobject. You can try adding this value into a Group instead." + ) assert len(obj.submobjects) == 1 - with pytest.raises(ValueError): - # a OpenGLMobject cannot contain itself + + # A VMobject or VGroup cannot contain itself. + with pytest.raises(ValueError) as add_self_info: obj.add(obj) + assert str(add_self_info.value) == ( + "Cannot add VGroup as a submobject of itself (at index 0)." + ) + assert len(obj.submobjects) == 1 def test_vgroup_add_dunder(using_opengl_renderer): @@ -192,7 +288,7 @@ def test_vgroup_supports_item_assigment(using_opengl_renderer): def test_vgroup_item_assignment_at_correct_position(using_opengl_renderer): - """Test VGroup item-assignment adds to correct position for OpenGLVMObjects""" + """Test VGroup item-assignment adds to correct position for OpenGLVMobjects""" n_items = 10 vgroup = VGroup() for _i in range(n_items): @@ -206,5 +302,9 @@ def test_vgroup_item_assignment_at_correct_position(using_opengl_renderer): def test_vgroup_item_assignment_only_allows_vmobjects(using_opengl_renderer): """Test VGroup item-assignment raises TypeError when invalid type is passed""" vgroup = VGroup(OpenGLVMobject()) - with pytest.raises(TypeError, match="All submobjects must be of type VMobject"): + with pytest.raises(TypeError) as assign_str_info: vgroup[0] = "invalid object" + assert str(assign_str_info.value) == ( + "Only values of type OpenGLVMobject can be added as submobjects of " + "VGroup, but the value invalid object (at index 0) is of type str." + ) From 21eb0b7769ec5cac2435802e74d00ae34a28e673 Mon Sep 17 00:00:00 2001 From: adeshpande <110117391+JasonGrace2282@users.noreply.github.com> Date: Wed, 22 May 2024 06:56:40 -0400 Subject: [PATCH 5/8] Add pyproject for ruff formatting (#3777) * Add pyproject for ruff * add black config back * Make only formatting * rearrange isort to undo diff * poetry lock * Feedback * style Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com> --------- Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com> --- poetry.lock | 449 ++++++++++++++++++++++++++----------------------- pyproject.toml | 22 +++ 2 files changed, 261 insertions(+), 210 deletions(-) diff --git a/poetry.lock b/poetry.lock index 557074cb1e..22d920cd9a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -708,43 +708,43 @@ toml = ["tomli"] [[package]] name = "cryptography" -version = "42.0.6" +version = "42.0.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.6-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:073104df012fc815eed976cd7d0a386c8725d0d0947cf9c37f6c36a6c20feb1b"}, - {file = "cryptography-42.0.6-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:5967e3632f42b0c0f9dc2c9da88c79eabdda317860b246d1fbbde4a8bbbc3b44"}, - {file = "cryptography-42.0.6-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99831397fdc6e6e0aa088b060c278c6e635d25c0d4d14bdf045bf81792fda0a"}, - {file = "cryptography-42.0.6-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:089aeb297ff89615934b22c7631448598495ffd775b7d540a55cfee35a677bf4"}, - {file = "cryptography-42.0.6-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:97eeacae9aa526ddafe68b9202a535f581e21d78f16688a84c8dcc063618e121"}, - {file = "cryptography-42.0.6-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f4cece02478d73dacd52be57a521d168af64ae03d2a567c0c4eb6f189c3b9d79"}, - {file = "cryptography-42.0.6-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:aeb6f56b004e898df5530fa873e598ec78eb338ba35f6fa1449970800b1d97c2"}, - {file = "cryptography-42.0.6-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:8b90c57b3cd6128e0863b894ce77bd36fcb5f430bf2377bc3678c2f56e232316"}, - {file = "cryptography-42.0.6-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d16a310c770cc49908c500c2ceb011f2840674101a587d39fa3ea828915b7e83"}, - {file = "cryptography-42.0.6-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e3442601d276bd9e961d618b799761b4e5d892f938e8a4fe1efbe2752be90455"}, - {file = "cryptography-42.0.6-cp37-abi3-win32.whl", hash = "sha256:00c0faa5b021457848d031ecff041262211cc1e2bce5f6e6e6c8108018f6b44a"}, - {file = "cryptography-42.0.6-cp37-abi3-win_amd64.whl", hash = "sha256:b16b90605c62bcb3aa7755d62cf5e746828cfc3f965a65211849e00c46f8348d"}, - {file = "cryptography-42.0.6-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:eecca86813c6a923cabff284b82ff4d73d9e91241dc176250192c3a9b9902a54"}, - {file = "cryptography-42.0.6-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d93080d2b01b292e7ee4d247bf93ed802b0100f5baa3fa5fd6d374716fa480d4"}, - {file = "cryptography-42.0.6-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ff75b88a4d273c06d968ad535e6cb6a039dd32db54fe36f05ed62ac3ef64a44"}, - {file = "cryptography-42.0.6-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c05230d8aaaa6b8ab3ab41394dc06eb3d916131df1c9dcb4c94e8f041f704b74"}, - {file = "cryptography-42.0.6-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9184aff0856261ecb566a3eb26a05dfe13a292c85ce5c59b04e4aa09e5814187"}, - {file = "cryptography-42.0.6-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:4bdb39ecbf05626e4bfa1efd773bb10346af297af14fb3f4c7cb91a1d2f34a46"}, - {file = "cryptography-42.0.6-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e85f433230add2aa26b66d018e21134000067d210c9c68ef7544ba65fc52e3eb"}, - {file = "cryptography-42.0.6-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:65d529c31bd65d54ce6b926a01e1b66eacf770b7e87c0622516a840e400ec732"}, - {file = "cryptography-42.0.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f1e933b238978ccfa77b1fee0a297b3c04983f4cb84ae1c33b0ea4ae08266cc9"}, - {file = "cryptography-42.0.6-cp39-abi3-win32.whl", hash = "sha256:bc954251edcd8a952eeaec8ae989fec7fe48109ab343138d537b7ea5bb41071a"}, - {file = "cryptography-42.0.6-cp39-abi3-win_amd64.whl", hash = "sha256:9f1a3bc2747166b0643b00e0b56cd9b661afc9d5ff963acaac7a9c7b2b1ef638"}, - {file = "cryptography-42.0.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:945a43ebf036dd4b43ebfbbd6b0f2db29ad3d39df824fb77476ca5777a9dde33"}, - {file = "cryptography-42.0.6-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f567a82b7c2b99257cca2a1c902c1b129787278ff67148f188784245c7ed5495"}, - {file = "cryptography-42.0.6-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3b750279f3e7715df6f68050707a0cee7cbe81ba2eeb2f21d081bd205885ffed"}, - {file = "cryptography-42.0.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6981acac509cc9415344cb5bfea8130096ea6ebcc917e75503143a1e9e829160"}, - {file = "cryptography-42.0.6-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:076c92b08dd1ab88108bc84545187e10d3693a9299c593f98c4ea195a0b0ead7"}, - {file = "cryptography-42.0.6-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81dbe47e28b703bc4711ac74a64ef8b758a0cf056ce81d08e39116ab4bc126fa"}, - {file = "cryptography-42.0.6-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e1f5f15c5ddadf6ee4d1d624a2ae940f14bd74536230b0056ccb28bb6248e42a"}, - {file = "cryptography-42.0.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:43e521f21c2458038d72e8cdfd4d4d9f1d00906a7b6636c4272e35f650d1699b"}, - {file = "cryptography-42.0.6.tar.gz", hash = "sha256:f987a244dfb0333fbd74a691c36000a2569eaf7c7cc2ac838f85f59f0588ddc9"}, + {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"}, + {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"}, + {file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"}, + {file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"}, + {file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"}, + {file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"}, + {file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"}, + {file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"}, ] [package.dependencies] @@ -1865,13 +1865,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.1.8" +version = "4.2.0" description = "JupyterLab computational environment" optional = true python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.1.8-py3-none-any.whl", hash = "sha256:c3baf3a2f91f89d110ed5786cd18672b9a357129d4e389d2a0dead15e11a4d2c"}, - {file = "jupyterlab-4.1.8.tar.gz", hash = "sha256:3384aded8680e7ce504fd63b8bb89a39df21c9c7694d9e7dc4a68742cdb30f9b"}, + {file = "jupyterlab-4.2.0-py3-none-any.whl", hash = "sha256:0dfe9278e25a145362289c555d9beb505697d269c10e99909766af7c440ad3cc"}, + {file = "jupyterlab-4.2.0.tar.gz", hash = "sha256:356e9205a6a2ab689c47c8fe4919dba6c076e376d03f26baadc05748c2435dd5"}, ] [package.dependencies] @@ -1891,11 +1891,11 @@ tornado = ">=6.2.0" traitlets = "*" [package.extras] -dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.2.0)"] +dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.3.5)"] docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<7.3.0)", "sphinx-copybutton"] -docs-screenshots = ["altair (==5.2.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.1)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.0.post6)", "matplotlib (==3.8.2)", "nbconvert (>=7.0.0)", "pandas (==2.2.0)", "scipy (==1.12.0)", "vega-datasets (==0.9.0)"] +docs-screenshots = ["altair (==5.3.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.2)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.1.post2)", "matplotlib (==3.8.3)", "nbconvert (>=7.0.0)", "pandas (==2.2.1)", "scipy (==1.12.0)", "vega-datasets (==0.9.0)"] test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"] -upgrade-extension = ["copier (>=8.0,<9.0)", "jinja2-time (<0.3)", "pydantic (<2.0)", "pyyaml-include (<2.0)", "tomli-w (<2.0)"] +upgrade-extension = ["copier (>=8,<10)", "jinja2-time (<0.3)", "pydantic (<2.0)", "pyyaml-include (<2.0)", "tomli-w (<2.0)"] [[package]] name = "jupyterlab-pygments" @@ -2249,39 +2249,40 @@ files = [ [[package]] name = "matplotlib" -version = "3.8.4" +version = "3.9.0" description = "Python plotting package" optional = false python-versions = ">=3.9" files = [ - {file = "matplotlib-3.8.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:abc9d838f93583650c35eca41cfcec65b2e7cb50fd486da6f0c49b5e1ed23014"}, - {file = "matplotlib-3.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f65c9f002d281a6e904976007b2d46a1ee2bcea3a68a8c12dda24709ddc9106"}, - {file = "matplotlib-3.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce1edd9f5383b504dbc26eeea404ed0a00656c526638129028b758fd43fc5f10"}, - {file = "matplotlib-3.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecd79298550cba13a43c340581a3ec9c707bd895a6a061a78fa2524660482fc0"}, - {file = "matplotlib-3.8.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:90df07db7b599fe7035d2f74ab7e438b656528c68ba6bb59b7dc46af39ee48ef"}, - {file = "matplotlib-3.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:ac24233e8f2939ac4fd2919eed1e9c0871eac8057666070e94cbf0b33dd9c338"}, - {file = "matplotlib-3.8.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:72f9322712e4562e792b2961971891b9fbbb0e525011e09ea0d1f416c4645661"}, - {file = "matplotlib-3.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:232ce322bfd020a434caaffbd9a95333f7c2491e59cfc014041d95e38ab90d1c"}, - {file = "matplotlib-3.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6addbd5b488aedb7f9bc19f91cd87ea476206f45d7116fcfe3d31416702a82fa"}, - {file = "matplotlib-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc4ccdc64e3039fc303defd119658148f2349239871db72cd74e2eeaa9b80b71"}, - {file = "matplotlib-3.8.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b7a2a253d3b36d90c8993b4620183b55665a429da8357a4f621e78cd48b2b30b"}, - {file = "matplotlib-3.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:8080d5081a86e690d7688ffa542532e87f224c38a6ed71f8fbed34dd1d9fedae"}, - {file = "matplotlib-3.8.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6485ac1f2e84676cff22e693eaa4fbed50ef5dc37173ce1f023daef4687df616"}, - {file = "matplotlib-3.8.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c89ee9314ef48c72fe92ce55c4e95f2f39d70208f9f1d9db4e64079420d8d732"}, - {file = "matplotlib-3.8.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50bac6e4d77e4262c4340d7a985c30912054745ec99756ce213bfbc3cb3808eb"}, - {file = "matplotlib-3.8.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f51c4c869d4b60d769f7b4406eec39596648d9d70246428745a681c327a8ad30"}, - {file = "matplotlib-3.8.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b12ba985837e4899b762b81f5b2845bd1a28f4fdd1a126d9ace64e9c4eb2fb25"}, - {file = "matplotlib-3.8.4-cp312-cp312-win_amd64.whl", hash = "sha256:7a6769f58ce51791b4cb8b4d7642489df347697cd3e23d88266aaaee93b41d9a"}, - {file = "matplotlib-3.8.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:843cbde2f0946dadd8c5c11c6d91847abd18ec76859dc319362a0964493f0ba6"}, - {file = "matplotlib-3.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c13f041a7178f9780fb61cc3a2b10423d5e125480e4be51beaf62b172413b67"}, - {file = "matplotlib-3.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb44f53af0a62dc80bba4443d9b27f2fde6acfdac281d95bc872dc148a6509cc"}, - {file = "matplotlib-3.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:606e3b90897554c989b1e38a258c626d46c873523de432b1462f295db13de6f9"}, - {file = "matplotlib-3.8.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9bb0189011785ea794ee827b68777db3ca3f93f3e339ea4d920315a0e5a78d54"}, - {file = "matplotlib-3.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:6209e5c9aaccc056e63b547a8152661324404dd92340a6e479b3a7f24b42a5d0"}, - {file = "matplotlib-3.8.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c7064120a59ce6f64103c9cefba8ffe6fba87f2c61d67c401186423c9a20fd35"}, - {file = "matplotlib-3.8.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0e47eda4eb2614300fc7bb4657fced3e83d6334d03da2173b09e447418d499f"}, - {file = "matplotlib-3.8.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:493e9f6aa5819156b58fce42b296ea31969f2aab71c5b680b4ea7a3cb5c07d94"}, - {file = "matplotlib-3.8.4.tar.gz", hash = "sha256:8aac397d5e9ec158960e31c381c5ffc52ddd52bd9a47717e2a694038167dffea"}, + {file = "matplotlib-3.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2bcee1dffaf60fe7656183ac2190bd630842ff87b3153afb3e384d966b57fe56"}, + {file = "matplotlib-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f988bafb0fa39d1074ddd5bacd958c853e11def40800c5824556eb630f94d3b"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe428e191ea016bb278758c8ee82a8129c51d81d8c4bc0846c09e7e8e9057241"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaf3978060a106fab40c328778b148f590e27f6fa3cd15a19d6892575bce387d"}, + {file = "matplotlib-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e7f03e5cbbfacdd48c8ea394d365d91ee8f3cae7e6ec611409927b5ed997ee4"}, + {file = "matplotlib-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:13beb4840317d45ffd4183a778685e215939be7b08616f431c7795276e067463"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:063af8587fceeac13b0936c42a2b6c732c2ab1c98d38abc3337e430e1ff75e38"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a2fa6d899e17ddca6d6526cf6e7ba677738bf2a6a9590d702c277204a7c6152"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550cdda3adbd596078cca7d13ed50b77879104e2e46392dcd7c75259d8f00e85"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cce0f31b351e3551d1f3779420cf8f6ec0d4a8cf9c0237a3b549fd28eb4abb"}, + {file = "matplotlib-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c53aeb514ccbbcbab55a27f912d79ea30ab21ee0531ee2c09f13800efb272674"}, + {file = "matplotlib-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5be985db2596d761cdf0c2eaf52396f26e6a64ab46bd8cd810c48972349d1be"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c79f3a585f1368da6049318bdf1f85568d8d04b2e89fc24b7e02cc9b62017382"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bdd1ecbe268eb3e7653e04f451635f0fb0f77f07fd070242b44c076c9106da84"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e85a1a6d732f645f1403ce5e6727fd9418cd4574521d5803d3d94911038e5"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a490715b3b9984fa609116481b22178348c1a220a4499cda79132000a79b4db"}, + {file = "matplotlib-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8146ce83cbc5dc71c223a74a1996d446cd35cfb6a04b683e1446b7e6c73603b7"}, + {file = "matplotlib-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:d91a4ffc587bacf5c4ce4ecfe4bcd23a4b675e76315f2866e588686cc97fccdf"}, + {file = "matplotlib-3.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:616fabf4981a3b3c5a15cd95eba359c8489c4e20e03717aea42866d8d0465956"}, + {file = "matplotlib-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd53c79fd02f1c1808d2cfc87dd3cf4dbc63c5244a58ee7944497107469c8d8a"}, + {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06a478f0d67636554fa78558cfbcd7b9dba85b51f5c3b5a0c9be49010cf5f321"}, + {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81c40af649d19c85f8073e25e5806926986806fa6d54be506fbf02aef47d5a89"}, + {file = "matplotlib-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52146fc3bd7813cc784562cb93a15788be0b2875c4655e2cc6ea646bfa30344b"}, + {file = "matplotlib-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:0fc51eaa5262553868461c083d9adadb11a6017315f3a757fc45ec6ec5f02888"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bd4f2831168afac55b881db82a7730992aa41c4f007f1913465fb182d6fb20c0"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:290d304e59be2b33ef5c2d768d0237f5bd132986bdcc66f80bc9bcc300066a03"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff2e239c26be4f24bfa45860c20ffccd118d270c5b5d081fa4ea409b5469fcd"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:af4001b7cae70f7eaacfb063db605280058246de590fa7874f00f62259f2df7e"}, + {file = "matplotlib-3.9.0.tar.gz", hash = "sha256:e6d29ea6c19e34b30fb7d88b7081f869a03014f66fe06d62cc77d5a6ea88ed7a"}, ] [package.dependencies] @@ -2290,12 +2291,15 @@ cycler = ">=0.10" fonttools = ">=4.22.0" importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} kiwisolver = ">=1.3.1" -numpy = ">=1.21" +numpy = ">=1.23" packaging = ">=20.0" pillow = ">=8" pyparsing = ">=2.3.1" python-dateutil = ">=2.7" +[package.extras] +dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] + [[package]] name = "matplotlib-inline" version = "0.1.7" @@ -2323,13 +2327,13 @@ files = [ [[package]] name = "mdit-py-plugins" -version = "0.4.0" +version = "0.4.1" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.8" files = [ - {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, - {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, + {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, + {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, ] [package.dependencies] @@ -2643,26 +2647,26 @@ setuptools = "*" [[package]] name = "notebook" -version = "7.1.3" +version = "7.2.0" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = true python-versions = ">=3.8" files = [ - {file = "notebook-7.1.3-py3-none-any.whl", hash = "sha256:919b911e59f41f6e3857ce93c9d93535ba66bb090059712770e5968c07e1004d"}, - {file = "notebook-7.1.3.tar.gz", hash = "sha256:41fcebff44cf7bb9377180808bcbae066629b55d8c7722f1ebbe75ca44f9cfc1"}, + {file = "notebook-7.2.0-py3-none-any.whl", hash = "sha256:b4752d7407d6c8872fc505df0f00d3cae46e8efb033b822adacbaa3f1f3ce8f5"}, + {file = "notebook-7.2.0.tar.gz", hash = "sha256:34a2ba4b08ad5d19ec930db7484fb79746a1784be9e1a5f8218f9af8656a141f"}, ] [package.dependencies] jupyter-server = ">=2.4.0,<3" -jupyterlab = ">=4.1.1,<4.2" -jupyterlab-server = ">=2.22.1,<3" +jupyterlab = ">=4.2.0,<4.3" +jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2,<0.3" tornado = ">=6.2.0" [package.extras] dev = ["hatch", "pre-commit"] docs = ["myst-parser", "nbsphinx", "pydata-sphinx-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.22.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] +test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.27.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] [[package]] name = "notebook-shim" @@ -2887,13 +2891,13 @@ xmp = ["defusedxml"] [[package]] name = "platformdirs" -version = "4.2.1" +version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] @@ -2918,13 +2922,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "3.7.0" +version = "3.7.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" files = [ - {file = "pre_commit-3.7.0-py2.py3-none-any.whl", hash = "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab"}, - {file = "pre_commit-3.7.0.tar.gz", hash = "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060"}, + {file = "pre_commit-3.7.1-py2.py3-none-any.whl", hash = "sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5"}, + {file = "pre_commit-3.7.1.tar.gz", hash = "sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a"}, ] [package.dependencies] @@ -3606,13 +3610,13 @@ rpds-py = ">=0.7.0" [[package]] name = "requests" -version = "2.32.0" +version = "2.32.1" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ - {file = "requests-2.32.0-py3-none-any.whl", hash = "sha256:f2c3881dddb70d056c5bd7600a4fae312b2a300e39be6a118d30b90bd27262b5"}, - {file = "requests-2.32.0.tar.gz", hash = "sha256:fa5490319474c82ef1d2c9bc459d3652e3ae4ef4c4ebdd18a21145a47ca4b6b8"}, + {file = "requests-2.32.1-py3-none-any.whl", hash = "sha256:21ac9465cdf8c1650fe1ecde8a71669a93d4e6f147550483a2967d08396a56a5"}, + {file = "requests-2.32.1.tar.gz", hash = "sha256:eb97e87e64c79e64e5b8ac75cee9dd1f97f49e289b083ee6be96268930725685"}, ] [package.dependencies] @@ -3683,110 +3687,136 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "rpds-py" -version = "0.18.0" +version = "0.18.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = true python-versions = ">=3.8" files = [ - {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, - {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, - {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, - {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, - {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, - {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, - {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, - {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, - {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, - {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, - {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, - {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, - {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, + {file = "rpds_py-0.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53"}, + {file = "rpds_py-0.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1"}, + {file = "rpds_py-0.18.1-cp310-none-win32.whl", hash = "sha256:c273e795e7a0f1fddd46e1e3cb8be15634c29ae8ff31c196debb620e1edb9333"}, + {file = "rpds_py-0.18.1-cp310-none-win_amd64.whl", hash = "sha256:8352f48d511de5f973e4f2f9412736d7dea76c69faa6d36bcf885b50c758ab9a"}, + {file = "rpds_py-0.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8"}, + {file = "rpds_py-0.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88"}, + {file = "rpds_py-0.18.1-cp311-none-win32.whl", hash = "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb"}, + {file = "rpds_py-0.18.1-cp311-none-win_amd64.whl", hash = "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2"}, + {file = "rpds_py-0.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3"}, + {file = "rpds_py-0.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a"}, + {file = "rpds_py-0.18.1-cp312-none-win32.whl", hash = "sha256:1805d5901779662d599d0e2e4159d8a82c0b05faa86ef9222bf974572286b2b6"}, + {file = "rpds_py-0.18.1-cp312-none-win_amd64.whl", hash = "sha256:720edcb916df872d80f80a1cc5ea9058300b97721efda8651efcd938a9c70a72"}, + {file = "rpds_py-0.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74"}, + {file = "rpds_py-0.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc"}, + {file = "rpds_py-0.18.1-cp38-none-win32.whl", hash = "sha256:312fe69b4fe1ffbe76520a7676b1e5ac06ddf7826d764cc10265c3b53f96dbe9"}, + {file = "rpds_py-0.18.1-cp38-none-win_amd64.whl", hash = "sha256:9437ca26784120a279f3137ee080b0e717012c42921eb07861b412340f85bae2"}, + {file = "rpds_py-0.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93"}, + {file = "rpds_py-0.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26"}, + {file = "rpds_py-0.18.1-cp39-none-win32.whl", hash = "sha256:2625f03b105328729f9450c8badda34d5243231eef6535f80064d57035738360"}, + {file = "rpds_py-0.18.1-cp39-none-win_amd64.whl", hash = "sha256:bf18932d0003c8c4d51a39f244231986ab23ee057d235a12b2684ea26a353590"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e"}, + {file = "rpds_py-0.18.1.tar.gz", hash = "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f"}, +] + +[[package]] +name = "ruff" +version = "0.4.4" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"}, + {file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"}, + {file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"}, + {file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"}, + {file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"}, + {file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"}, + {file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"}, + {file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"}, ] [[package]] @@ -3864,19 +3894,18 @@ win32 = ["pywin32"] [[package]] name = "setuptools" -version = "69.5.1" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -4346,24 +4375,24 @@ files = [ [[package]] name = "types-pillow" -version = "10.2.0.20240423" +version = "10.2.0.20240520" description = "Typing stubs for Pillow" optional = false python-versions = ">=3.8" files = [ - {file = "types-Pillow-10.2.0.20240423.tar.gz", hash = "sha256:696e68b9b6a58548fc307a8669830469237c5b11809ddf978ac77fafa79251cd"}, - {file = "types_Pillow-10.2.0.20240423-py3-none-any.whl", hash = "sha256:bd12923093b96c91d523efcdb66967a307f1a843bcfaf2d5a529146c10a9ced3"}, + {file = "types-Pillow-10.2.0.20240520.tar.gz", hash = "sha256:130b979195465fa1e1676d8e81c9c7c30319e8e95b12fae945e8f0d525213107"}, + {file = "types_Pillow-10.2.0.20240520-py3-none-any.whl", hash = "sha256:33c36494b380e2a269bb742181bea5d9b00820367822dbd3760f07210a1da23d"}, ] [[package]] name = "types-pygments" -version = "2.17.0.20240310" +version = "2.18.0.20240506" description = "Typing stubs for Pygments" optional = false python-versions = ">=3.8" files = [ - {file = "types-Pygments-2.17.0.20240310.tar.gz", hash = "sha256:b1d97e905ce36343c7283b0319182ae6d4f967188f361f45502a18ae43e03e1f"}, - {file = "types_Pygments-2.17.0.20240310-py3-none-any.whl", hash = "sha256:b101ca9448aaff52af6966506f1fdd73b1e60a79b8a79a8bace3366cbf1f7ed9"}, + {file = "types-Pygments-2.18.0.20240506.tar.gz", hash = "sha256:4b4c37812c87bbde687dbf27adf5bac593745a321e57f678dbc311571ba2ac9d"}, + {file = "types_Pygments-2.18.0.20240506-py3-none-any.whl", hash = "sha256:11c90bc1737c9af55e5569558b88df7c2233e12325cb516215f722271444e91d"}, ] [package.dependencies] @@ -4383,13 +4412,13 @@ files = [ [[package]] name = "types-setuptools" -version = "69.5.0.20240423" +version = "69.5.0.20240519" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.8" files = [ - {file = "types-setuptools-69.5.0.20240423.tar.gz", hash = "sha256:a7ba908f1746c4337d13f027fa0f4a5bcad6d1d92048219ba792b3295c58586d"}, - {file = "types_setuptools-69.5.0.20240423-py3-none-any.whl", hash = "sha256:a4381e041510755a6c9210e26ad55b1629bc10237aeb9cb8b6bd24996b73db48"}, + {file = "types-setuptools-69.5.0.20240519.tar.gz", hash = "sha256:275fb72048b0203d3fbef268298ea78a0913cd114a74872d93f8638ccc5b7c63"}, + {file = "types_setuptools-69.5.0.20240519-py3-none-any.whl", hash = "sha256:52b264eff8913b5d85848d83bd98efea935fc6129d681d370eb957783880b720"}, ] [[package]] @@ -4436,13 +4465,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.1" +version = "20.26.2" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.1-py3-none-any.whl", hash = "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75"}, - {file = "virtualenv-20.26.1.tar.gz", hash = "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b"}, + {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, + {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, ] [package.dependencies] @@ -4629,18 +4658,18 @@ files = [ [[package]] name = "zipp" -version = "3.18.1" +version = "3.18.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, - {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, + {file = "zipp-3.18.2-py3-none-any.whl", hash = "sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e"}, + {file = "zipp-3.18.2.tar.gz", hash = "sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [extras] gui = ["dearpygui"] @@ -4649,4 +4678,4 @@ jupyterlab = ["jupyterlab", "notebook"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "0ff94622805480bc3b254c0df42a18e36b473a273b07af5de32726f53c34b2ad" +content-hash = "482eafcb61a2e3c6f64af915960bb4ae1298620c2be1bd5201fa47907e2eec94" diff --git a/pyproject.toml b/pyproject.toml index fbcc907511..7b05d004df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,6 +83,7 @@ pytest = "^7.4.3" pygithub = "^2.1.1" pytest-cov = "^4.1.0" pytest-xdist = "^2.2" # Using latest gives flaky tests +ruff = "*" Sphinx = "^7.2.6" sphinx-copybutton = "^0.5.2" sphinxcontrib-programoutput = "^0.17" @@ -118,3 +119,24 @@ exclude_lines = ["pragma: no cover"] [build-system] requires = ["setuptools", "poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" + +[tool.ruff] +line-length = 88 +target-version = "py39" +extend-exclude = [ + ".github", + ".hg", + ".env", + "env", + "build", + "buck-out", + "media", +] +fix = true + +[tool.ruff.format] +docstring-code-format = true + +[tool.codespell] +write-changes = true +ignore-words-list = ["medias", "nam"] From 24025b60d57301b0a59754c38d77bccd8ed69feb Mon Sep 17 00:00:00 2001 From: adeshpande <110117391+JasonGrace2282@users.noreply.github.com> Date: Wed, 22 May 2024 12:03:23 -0400 Subject: [PATCH 6/8] pre-commit change to ruff (#3779) * pre-commit change to ruff * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixes * astral-sh ruff bump --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 13 ++-- example_scenes/basic.py | 2 +- manim/_config/__init__.py | 1 - manim/_config/cli_colors.py | 9 ++- manim/mobject/geometry/tips.py | 5 +- manim/mobject/graph.py | 3 +- manim/mobject/graphing/coordinate_systems.py | 14 +++- manim/mobject/graphing/number_line.py | 8 +-- manim/mobject/mobject.py | 2 +- manim/mobject/text/text_mobject.py | 21 +++--- manim/mobject/types/vectorized_mobject.py | 24 ++++--- manim/scene/scene.py | 3 +- manim/scene/scene_file_writer.py | 1 - manim/utils/color/X11.py | 1 + manim/utils/color/core.py | 3 +- manim/utils/deprecation.py | 70 ++++++++++++------- manim/utils/docbuild/module_parsing.py | 3 +- manim/utils/space_ops.py | 2 +- tests/module/mobject/text/test_markup.py | 4 +- tests/opengl/test_markup_opengl.py | 4 +- .../opengl/test_cli_flags_opengl.py | 4 +- tests/test_scene_rendering/test_cli_flags.py | 4 +- 22 files changed, 113 insertions(+), 88 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cdf84ef892..de2a017cfc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,16 +34,11 @@ repos: hooks: - id: python-check-blanket-noqa name: Precision flake ignores - - repo: https://github.com/psf/black - rev: 24.4.2 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.4 hooks: - - id: black - - repo: https://github.com/asottile/blacken-docs - rev: 1.16.0 - hooks: - - id: blacken-docs - additional_dependencies: [black==24.4.0] - exclude: ^\.github/ + - id: ruff-format + types: [python] - repo: https://github.com/PyCQA/flake8 rev: 7.0.0 hooks: diff --git a/example_scenes/basic.py b/example_scenes/basic.py index e61ae01f67..6e8c866f49 100644 --- a/example_scenes/basic.py +++ b/example_scenes/basic.py @@ -153,7 +153,7 @@ def construct(self): ], color=PURPLE_B, fill_opacity=1, - stroke_width=0 + stroke_width=0, ).shift(UP + 2 * RIGHT) shapes = VGroup(triangle, square, circle, pentagon, pi) self.play(SpiralIn(shapes, fade_in_fraction=0.9)) diff --git a/manim/_config/__init__.py b/manim/_config/__init__.py index 18a57307b4..3eed54b481 100644 --- a/manim/_config/__init__.py +++ b/manim/_config/__init__.py @@ -68,7 +68,6 @@ def tempconfig(temp: ManimConfig | dict[str, Any]) -> Generator[None, None, None 8.0 >>> with tempconfig({"frame_height": 100.0}): ... print(config["frame_height"]) - ... 100.0 >>> config["frame_height"] 8.0 diff --git a/manim/_config/cli_colors.py b/manim/_config/cli_colors.py index e1d9fb02f8..c39f24d716 100644 --- a/manim/_config/cli_colors.py +++ b/manim/_config/cli_colors.py @@ -35,15 +35,18 @@ def parse_cli_ctx(parser: configparser.SectionProxy) -> Context: theme = parser["theme"] if parser["theme"] else None if theme is None: formatter = HelpFormatter.settings( - theme=HelpTheme(**theme_settings), **formatter_settings # type: ignore[arg-type] + theme=HelpTheme(**theme_settings), + **formatter_settings, # type: ignore[arg-type] ) elif theme.lower() == "dark": formatter = HelpFormatter.settings( - theme=HelpTheme.dark().with_(**theme_settings), **formatter_settings # type: ignore[arg-type] + theme=HelpTheme.dark().with_(**theme_settings), + **formatter_settings, # type: ignore[arg-type] ) elif theme.lower() == "light": formatter = HelpFormatter.settings( - theme=HelpTheme.light().with_(**theme_settings), **formatter_settings # type: ignore[arg-type] + theme=HelpTheme.light().with_(**theme_settings), + **formatter_settings, # type: ignore[arg-type] ) return Context.settings( diff --git a/manim/mobject/geometry/tips.py b/manim/mobject/geometry/tips.py index 725e9535ab..a7f116d3bf 100644 --- a/manim/mobject/geometry/tips.py +++ b/manim/mobject/geometry/tips.py @@ -60,8 +60,9 @@ class ArrowTip(VMobject, metaclass=ConvertToOpenGL): ... RegularPolygon.__init__(self, n=5, **kwargs) ... self.width = length ... self.stretch_to_fit_height(length) - >>> arr = Arrow(np.array([-2, -2, 0]), np.array([2, 2, 0]), - ... tip_shape=MyCustomArrowTip) + >>> arr = Arrow( + ... np.array([-2, -2, 0]), np.array([2, 2, 0]), tip_shape=MyCustomArrowTip + ... ) >>> isinstance(arr.tip, RegularPolygon) True >>> from manim import Scene, Create diff --git a/manim/mobject/graph.py b/manim/mobject/graph.py index bc11ec69e6..53d7ed464a 100644 --- a/manim/mobject/graph.py +++ b/manim/mobject/graph.py @@ -1513,7 +1513,8 @@ def expand_vertex(self, g, vertex_id: str, depth: int): *new_edges, vertex_config=self.VERTEX_CONF, positions={ - k: g.vertices[vertex_id].get_center() + 0.1 * DOWN for k in new_vertices + k: g.vertices[vertex_id].get_center() + 0.1 * DOWN + for k in new_vertices }, ) if depth < self.DEPTH: diff --git a/manim/mobject/graphing/coordinate_systems.py b/manim/mobject/graphing/coordinate_systems.py index 6315093f2c..3330193a43 100644 --- a/manim/mobject/graphing/coordinate_systems.py +++ b/manim/mobject/graphing/coordinate_systems.py @@ -394,7 +394,9 @@ def add_coordinates( ax = ThreeDAxes() x_labels = range(-4, 5) z_labels = range(-4, 4, 2) - ax.add_coordinates(x_labels, None, z_labels) # default y labels, custom x & z labels + ax.add_coordinates( + x_labels, None, z_labels + ) # default y labels, custom x & z labels ax.add_coordinates(x_labels) # only x labels You can also specifically control the position and value of the labels using a dict. @@ -405,7 +407,15 @@ def add_coordinates( x_pos = [x for x in range(1, 8)] # strings are automatically converted into a Tex mobject. - x_vals = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] + x_vals = [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", + ] x_dict = dict(zip(x_pos, x_vals)) ax.add_coordinates(x_dict) """ diff --git a/manim/mobject/graphing/number_line.py b/manim/mobject/graphing/number_line.py index 63c8954b14..834f3086c2 100644 --- a/manim/mobject/graphing/number_line.py +++ b/manim/mobject/graphing/number_line.py @@ -364,7 +364,7 @@ def number_to_point(self, number: float | np.ndarray) -> np.ndarray: array([0., 0., 0.]) >>> number_line.number_to_point(1) array([1., 0., 0.]) - >>> number_line.number_to_point([1,2,3]) + >>> number_line.number_to_point([1, 2, 3]) array([[1., 0., 0.], [2., 0., 0.], [3., 0., 0.]]) @@ -396,11 +396,11 @@ def point_to_number(self, point: Sequence[float]) -> float: >>> from manim import NumberLine >>> number_line = NumberLine() - >>> number_line.point_to_number((0,0,0)) + >>> number_line.point_to_number((0, 0, 0)) 0.0 - >>> number_line.point_to_number((1,0,0)) + >>> number_line.point_to_number((1, 0, 0)) 1.0 - >>> number_line.point_to_number([[0.5,0,0],[1,0,0],[1.5,0,0]]) + >>> number_line.point_to_number([[0.5, 0, 0], [1, 0, 0], [1.5, 0, 0]]) array([0.5, 1. , 1.5]) """ diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 0ae2a29daa..6e405a18eb 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -2064,7 +2064,7 @@ def get_critical_point(self, direction: Vector3D) -> Point3D: :: - sample = Arc(start_angle=PI/7, angle = PI/5) + sample = Arc(start_angle=PI / 7, angle=PI / 5) # These are all equivalent max_y_1 = sample.get_top()[1] diff --git a/manim/mobject/text/text_mobject.py b/manim/mobject/text/text_mobject.py index 971200a117..39a1cdd171 100644 --- a/manim/mobject/text/text_mobject.py +++ b/manim/mobject/text/text_mobject.py @@ -135,10 +135,17 @@ class Paragraph(VGroup): -------- Normal usage:: - paragraph = Paragraph('this is a awesome', 'paragraph', - 'With \nNewlines', '\tWith Tabs', - ' With Spaces', 'With Alignments', - 'center', 'left', 'right') + paragraph = Paragraph( + "this is a awesome", + "paragraph", + "With \nNewlines", + "\tWith Tabs", + " With Spaces", + "With Alignments", + "center", + "left", + "right", + ) Remove unwanted invisible characters:: @@ -1305,15 +1312,13 @@ def add_line_to(end): self.set_color_by_gradient(*self.gradient) for col in colormap: self.chars[ - col["start"] - - col["start_offset"] : col["end"] + col["start"] - col["start_offset"] : col["end"] - col["start_offset"] - col["end_offset"] ].set_color(self._parse_color(col["color"])) for grad in gradientmap: self.chars[ - grad["start"] - - grad["start_offset"] : grad["end"] + grad["start"] - grad["start_offset"] : grad["end"] - grad["start_offset"] - grad["end_offset"] ].set_color_by_gradient( diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index d82346868a..f32e88aaa2 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -1926,19 +1926,21 @@ class VGroup(VMobject, metaclass=ConvertToOpenGL): >>> triangle, square = Triangle(), Square() >>> vg.add(triangle) VGroup(Triangle) - >>> vg + square # a new VGroup is constructed + >>> vg + square # a new VGroup is constructed VGroup(Triangle, Square) - >>> vg # not modified + >>> vg # not modified VGroup(Triangle) - >>> vg += square; vg # modifies vg + >>> vg += square + >>> vg # modifies vg VGroup(Triangle, Square) >>> vg.remove(triangle) VGroup(Square) - >>> vg - square; # a new VGroup is constructed + >>> vg - square # a new VGroup is constructed VGroup() - >>> vg # not modified + >>> vg # not modified VGroup(Square) - >>> vg -= square; vg # modifies vg + >>> vg -= square + >>> vg # modifies vg VGroup() .. manim:: ArcShapeIris @@ -2200,7 +2202,7 @@ def add( Normal usage:: square_obj = Square() - my_dict.add([('s', square_obj)]) + my_dict.add([("s", square_obj)]) """ for key, value in dict(mapping_or_iterable).items(): self.add_key_value_pair(key, value) @@ -2227,7 +2229,7 @@ def remove(self, key: Hashable) -> Self: -------- Normal usage:: - my_dict.remove('square') + my_dict.remove("square") """ if key not in self.submob_dict: raise KeyError("The given key '%s' is not present in the VDict" % str(key)) @@ -2252,7 +2254,7 @@ def __getitem__(self, key: Hashable): -------- Normal usage:: - self.play(Create(my_dict['s'])) + self.play(Create(my_dict["s"])) """ submob = self.submob_dict[key] return submob @@ -2276,7 +2278,7 @@ def __setitem__(self, key: Hashable, value: VMobject) -> None: Normal usage:: square_obj = Square() - my_dict['sq'] = square_obj + my_dict["sq"] = square_obj """ if key in self.submob_dict: self.remove(key) @@ -2381,7 +2383,7 @@ def add_key_value_pair(self, key: Hashable, value: VMobject) -> None: Normal usage:: square_obj = Square() - self.add_key_value_pair('s', square_obj) + self.add_key_value_pair("s", square_obj) """ self._assert_valid_submobjects([value]) diff --git a/manim/scene/scene.py b/manim/scene/scene.py index 06dd15fc90..1f5faed4ec 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -1555,8 +1555,7 @@ def construct(self): # second option: within the call to Scene.play self.play( - Transform(square, circle), - subcaption="The square transforms." + Transform(square, circle), subcaption="The square transforms." ) """ diff --git a/manim/scene/scene_file_writer.py b/manim/scene/scene_file_writer.py index ebd7d13cac..3b58d2c73a 100644 --- a/manim/scene/scene_file_writer.py +++ b/manim/scene/scene_file_writer.py @@ -715,7 +715,6 @@ def combine_to_movie(self): av.open(movie_file_path) as video_input, av.open(sound_file_path) as audio_input, ): - video_stream = video_input.streams.video[0] audio_stream = audio_input.streams.audio[0] output_container = av.open( diff --git a/manim/utils/color/X11.py b/manim/utils/color/X11.py index 06d7c7575f..0379717eac 100644 --- a/manim/utils/color/X11.py +++ b/manim/utils/color/X11.py @@ -22,6 +22,7 @@ .. automanimcolormodule:: manim.utils.color.X11 """ + from .core import ManimColor ALICEBLUE = ManimColor("#F0F8FF") diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index d2312c6f53..32c895d100 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -147,7 +147,8 @@ def __init__( else: if length == 3: self._internal_value = ManimColor._internal_from_int_rgb( - value, alpha # type: ignore + value, + alpha, # type: ignore ) elif length == 4: self._internal_value = ManimColor._internal_from_int_rgba(value) # type: ignore diff --git a/manim/utils/deprecation.py b/manim/utils/deprecation.py index ece4d40340..2d443c5e72 100644 --- a/manim/utils/deprecation.py +++ b/manim/utils/deprecation.py @@ -105,10 +105,12 @@ def deprecated( from manim.utils.deprecation import deprecated + @deprecated def foo(**kwargs): pass + @deprecated class Bar: def __init__(self): @@ -118,6 +120,7 @@ def __init__(self): def baz(self): pass + foo() # WARNING The function foo has been deprecated and may be removed in a later version. @@ -131,15 +134,12 @@ def baz(self): from manim.utils.deprecation import deprecated - @deprecated( - since="v0.2", - until="v0.4", - replacement="bar", - message="It is cooler." - ) + + @deprecated(since="v0.2", until="v0.4", replacement="bar", message="It is cooler.") def foo(): pass + foo() # WARNING The function foo has been deprecated since v0.2 and is expected to be removed after v0.4. Use bar instead. It is cooler. @@ -147,10 +147,12 @@ def foo(): from manim.utils.deprecation import deprecated + @deprecated(since="05/01/2021", until="06/01/2021") def foo(): pass + foo() # WARNING The function foo has been deprecated since 05/01/2021 and is expected to be removed after 06/01/2021. @@ -234,9 +236,8 @@ def deprecated_params( since: str | None = None, until: str | None = None, message: str | None = "", - redirections: None | ( - Iterable[tuple[str, str] | Callable[..., dict[str, Any]]] - ) = None, + redirections: None + | (Iterable[tuple[str, str] | Callable[..., dict[str, Any]]]) = None, ) -> Callable: """Decorator to mark parameters of a callable as deprecated. @@ -287,10 +288,12 @@ def deprecated_params( from manim.utils.deprecation import deprecated_params + @deprecated_params(params="a, b, c") def foo(**kwargs): pass + foo(x=2, y=3, z=4) # No warning @@ -301,15 +304,17 @@ def foo(**kwargs): from manim.utils.deprecation import deprecated_params + @deprecated_params( params="a, b, c", since="v0.2", until="v0.4", - message="The letters x, y, z are cooler." + message="The letters x, y, z are cooler.", ) def foo(**kwargs): pass + foo(a=2) # WARNING The parameter a of method foo has been deprecated since v0.2 and is expected to be removed after v0.4. The letters x, y, z are cooler. @@ -317,14 +322,18 @@ def foo(**kwargs): from manim.utils.deprecation import deprecated_params - @deprecated_params(redirections=[ - # Two ways to redirect one parameter to another: - ("old_param", "new_param"), - lambda old_param2: {"new_param22": old_param2} - ]) + + @deprecated_params( + redirections=[ + # Two ways to redirect one parameter to another: + ("old_param", "new_param"), + lambda old_param2: {"new_param22": old_param2}, + ] + ) def foo(**kwargs): return kwargs + foo(x=1, old_param=2) # WARNING The parameter old_param of method foo has been deprecated and may be removed in a later version. # returns {"x": 1, "new_param": 2} @@ -333,12 +342,14 @@ def foo(**kwargs): from manim.utils.deprecation import deprecated_params - @deprecated_params(redirections=[ - lambda runtime_in_ms: {"run_time": runtime_in_ms / 1000} - ]) + + @deprecated_params( + redirections=[lambda runtime_in_ms: {"run_time": runtime_in_ms / 1000}] + ) def foo(**kwargs): return kwargs + foo(runtime_in_ms=500) # WARNING The parameter runtime_in_ms of method foo has been deprecated and may be removed in a later version. # returns {"run_time": 0.5} @@ -347,12 +358,14 @@ def foo(**kwargs): from manim.utils.deprecation import deprecated_params - @deprecated_params(redirections=[ - lambda buff_x=1, buff_y=1: {"buff": (buff_x, buff_y)} - ]) + + @deprecated_params( + redirections=[lambda buff_x=1, buff_y=1: {"buff": (buff_x, buff_y)}] + ) def foo(**kwargs): return kwargs + foo(buff_x=2) # WARNING The parameter buff_x of method foo has been deprecated and may be removed in a later version. # returns {"buff": (2, 1)} @@ -361,18 +374,23 @@ def foo(**kwargs): from manim.utils.deprecation import deprecated_params - @deprecated_params(redirections=[ - lambda buff=1: {"buff_x": buff[0], "buff_y": buff[1]} if isinstance(buff, tuple) - else {"buff_x": buff, "buff_y": buff} - ]) + + @deprecated_params( + redirections=[ + lambda buff=1: {"buff_x": buff[0], "buff_y": buff[1]} + if isinstance(buff, tuple) + else {"buff_x": buff, "buff_y": buff} + ] + ) def foo(**kwargs): return kwargs + foo(buff=0) # WARNING The parameter buff of method foo has been deprecated and may be removed in a later version. # returns {"buff_x": 0, buff_y: 0} - foo(buff=(1,2)) + foo(buff=(1, 2)) # WARNING The parameter buff of method foo has been deprecated and may be removed in a later version. # returns {"buff_x": 1, buff_y: 2} diff --git a/manim/utils/docbuild/module_parsing.py b/manim/utils/docbuild/module_parsing.py index 8ff7c0fe5e..87bfa76c40 100644 --- a/manim/utils/docbuild/module_parsing.py +++ b/manim/utils/docbuild/module_parsing.py @@ -125,8 +125,7 @@ def parse_module_attributes() -> tuple[AliasDocsDict, DataDict]: and ( ( # if TYPE_CHECKING - type(node.test) is ast.Name - and node.test.id == "TYPE_CHECKING" + type(node.test) is ast.Name and node.test.id == "TYPE_CHECKING" ) or ( # if typing.TYPE_CHECKING diff --git a/manim/utils/space_ops.py b/manim/utils/space_ops.py index 3df699beca..ec47d136eb 100644 --- a/manim/utils/space_ops.py +++ b/manim/utils/space_ops.py @@ -622,7 +622,7 @@ def get_winding_number(points: Sequence[np.ndarray]) -> float: >>> polygon = Square() >>> get_winding_number(polygon.get_vertices()) 1.0 - >>> polygon.shift(2*UP) + >>> polygon.shift(2 * UP) Square >>> get_winding_number(polygon.get_vertices()) 0.0 diff --git a/tests/module/mobject/text/test_markup.py b/tests/module/mobject/text/test_markup.py index abe1d9333b..78c42ed187 100644 --- a/tests/module/mobject/text/test_markup.py +++ b/tests/module/mobject/text/test_markup.py @@ -22,9 +22,7 @@ def test_special_tags_markup(): success = True except ValueError: success = False - assert ( - success - ), '\'foo\' and \'foo\' should not fail validation' + assert success, '\'foo\' and \'foo\' should not fail validation' def test_unbalanced_tag_markup(): diff --git a/tests/opengl/test_markup_opengl.py b/tests/opengl/test_markup_opengl.py index b8a0e1c9fd..b96acbeb97 100644 --- a/tests/opengl/test_markup_opengl.py +++ b/tests/opengl/test_markup_opengl.py @@ -22,9 +22,7 @@ def test_special_tags_markup(using_opengl_renderer): success = True except ValueError: success = False - assert ( - success - ), '\'foo\' and \'foo\' should not fail validation' + assert success, '\'foo\' and \'foo\' should not fail validation' def test_unbalanced_tag_markup(using_opengl_renderer): diff --git a/tests/test_scene_rendering/opengl/test_cli_flags_opengl.py b/tests/test_scene_rendering/opengl/test_cli_flags_opengl.py index e9a5d7e96d..5b3a299dc5 100644 --- a/tests/test_scene_rendering/opengl/test_cli_flags_opengl.py +++ b/tests/test_scene_rendering/opengl/test_cli_flags_opengl.py @@ -342,9 +342,7 @@ def test_a_flag(tmp_path, manim_cfg_file, infallible_scenes_path): two_is_not_empty = ( tmp_path / "images" / "infallible_scenes" / f"Wait2_ManimCE_v{__version__}.png" ).is_file() - assert ( - two_is_not_empty - ), "running manim with -a flag did not render an image, possible leak of the config dictionary" + assert two_is_not_empty, "running manim with -a flag did not render an image, possible leak of the config dictionary" three_is_not_empty = ( tmp_path / "videos" / "infallible_scenes" / "480p15" / "Wait3.mp4" diff --git a/tests/test_scene_rendering/test_cli_flags.py b/tests/test_scene_rendering/test_cli_flags.py index aaee0276a2..c9516c6fc4 100644 --- a/tests/test_scene_rendering/test_cli_flags.py +++ b/tests/test_scene_rendering/test_cli_flags.py @@ -239,9 +239,7 @@ def test_a_flag(tmp_path, manim_cfg_file, infallible_scenes_path): two_is_not_empty = ( tmp_path / "images" / "infallible_scenes" / f"Wait2_ManimCE_v{__version__}.png" ).is_file() - assert ( - two_is_not_empty - ), "running manim with -a flag did not render an image, possible leak of the config dictionary." + assert two_is_not_empty, "running manim with -a flag did not render an image, possible leak of the config dictionary." three_is_not_empty = ( tmp_path / "videos" / "infallible_scenes" / "480p15" / "Wait3.mp4" From c9776be7dafc54f444e9ee7c7d060d7267290f30 Mon Sep 17 00:00:00 2001 From: Victorien <65306057+Viicos@users.noreply.github.com> Date: Wed, 22 May 2024 19:32:50 +0200 Subject: [PATCH 7/8] Ignore Ruff format in git blame (#3781) --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..bfe85b8cae --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Switched to ruff format: +24025b60d57301b0a59754c38d77bccd8ed69feb From 0a2fbbeffb0921f01a5eedf4a113f35f4840a838 Mon Sep 17 00:00:00 2001 From: jkjkil4 <52841865+jkjkil4@users.noreply.github.com> Date: Thu, 23 May 2024 02:22:35 +0800 Subject: [PATCH 8/8] Fixed `there_and_back_with_pause()` rate function behaviour with different `pause_ratio` values (#3778) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com> --- manim/utils/rate_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/utils/rate_functions.py b/manim/utils/rate_functions.py index d3cd79c355..ab8cabc989 100644 --- a/manim/utils/rate_functions.py +++ b/manim/utils/rate_functions.py @@ -217,7 +217,7 @@ def there_and_back(t: float, inflection: float = 10.0) -> float: @zero def there_and_back_with_pause(t: float, pause_ratio: float = 1.0 / 3) -> float: - a = 1.0 / pause_ratio + a = 2.0 / (1.0 - pause_ratio) if t < 0.5 - pause_ratio / 2: return smooth(a * t) elif t < 0.5 + pause_ratio / 2: