From df961fa3c1b7bb7cb6918d3ef89462ae640d7631 Mon Sep 17 00:00:00 2001 From: Wey Gu Date: Tue, 21 Mar 2023 19:27:23 +0800 Subject: [PATCH] feat: add tests pip3 install pdm pdm install pdm run test pdm run lint pdm run format --- .gitignore | 6 +- README.md | 15 ++ ng_ai/__init__.py | 7 +- ng_ai/engines.py | 9 +- ng_ai/nebula_algo.py | 92 ++++--- ng_ai/nebula_data.py | 2 +- ng_ai/nebula_reader.py | 5 +- ng_ai/nebula_writer.py | 19 +- ng_ai/ng_ai_api/__main__.py | 4 +- ng_ai/ng_ai_api/app.py | 23 +- pdm.lock | 524 +++++++++++++++++++++++++----------- pyproject.toml | 66 ++++- tests/test_nebula_algo.py | 60 +++++ tests/test_nebula_data.py | 61 +++++ tests/test_nebula_reader.py | 63 +++++ tests/test_nebula_writer.py | 114 ++++++++ 16 files changed, 843 insertions(+), 227 deletions(-) create mode 100644 tests/test_nebula_algo.py create mode 100644 tests/test_nebula_data.py create mode 100644 tests/test_nebula_reader.py create mode 100644 tests/test_nebula_writer.py diff --git a/.gitignore b/.gitignore index 31f289d..204d745 100644 --- a/.gitignore +++ b/.gitignore @@ -133,4 +133,8 @@ dmypy.json .pdm.toml # requirments.txt is only for local development -requirements.txt \ No newline at end of file +requirements.txt + +# ide + +.vscode \ No newline at end of file diff --git a/README.md b/README.md index 9dcad06..c094b11 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,21 @@ ng_ai is an unified abstraction layer for different engines, the current impleme - [NebulaGraph Python Client 3.4+](https://github.com/vesoft-inc/nebula-python) - [NetworkX](https://networkx.org/) + +## Contributing + +```bash +pip3 install pdm +# build and install ng_ai +pdm install +# run tests +pdm run test +# lint +pdm run lint +# format +pdm run format +``` + ## License This project is licensed under the terms of the Apache License 2.0. diff --git a/ng_ai/__init__.py b/ng_ai/__init__.py index b27ab8d..7b26819 100644 --- a/ng_ai/__init__.py +++ b/ng_ai/__init__.py @@ -3,14 +3,13 @@ from pkgutil import extend_path - __path__ = extend_path(__path__, __name__) # type: ignore -from ng_ai.nebula_reader import NebulaReader -from ng_ai.nebula_writer import NebulaWriter +from ng_ai.config import NebulaGraphConfig from ng_ai.nebula_algo import NebulaAlgorithm from ng_ai.nebula_gnn import NebulaGNN -from ng_ai.config import NebulaGraphConfig +from ng_ai.nebula_reader import NebulaReader +from ng_ai.nebula_writer import NebulaWriter from ng_ai.ng_ai_api.app import app as ng_ai_api_app # export diff --git a/ng_ai/engines.py b/ng_ai/engines.py index 80f94ef..8799bda 100644 --- a/ng_ai/engines.py +++ b/ng_ai/engines.py @@ -2,8 +2,6 @@ # Copyright 2023 The NebulaGraph Authors. All rights reserved. from __future__ import annotations -from ng_ai.config import NebulaGraphConfig - # SPARK DEFAULTS DEFAULT_SHUFFLE_PARTITIONS = 5 DEFAULT_EXECUTOR_MEMORY = "8g" @@ -58,7 +56,8 @@ def __init__(self, config): self.prepare() self.nebula_spark_ds = NEBULA_SPARK_CONNECTOR_DATASOURCE - # TBD: ping NebulaGraph Meta and Storage Server, fail and guide user to check config + # TBD: ping NebulaGraph Meta and Storage Server + # fail and guide user to check config def __str__(self): return f"SparkEngine: {self.spark}" @@ -91,7 +90,9 @@ def _get_java_import(self, force=False): # scala: # import "com.vesoft.nebula.algorithm.config.SparkConfig" - java_import(self.spark._jvm, "com.vesoft.nebula.algorithm.config.SparkConfig") + java_import( + self.spark._jvm, "com.vesoft.nebula.algorithm.config.SparkConfig" + ) return java_import def import_scala_class(self, class_name): diff --git a/ng_ai/nebula_algo.py b/ng_ai/nebula_algo.py index c3fc396..6369cef 100644 --- a/ng_ai/nebula_algo.py +++ b/ng_ai/nebula_algo.py @@ -3,8 +3,8 @@ from __future__ import annotations -from ng_ai.nebula_data import NebulaGraphObject as NebulaGraphObjectImpl from ng_ai.nebula_data import NebulaDataFrameObject as NebulaDataFrameObjectImpl +from ng_ai.nebula_data import NebulaGraphObject as NebulaGraphObjectImpl def algo(func): @@ -53,13 +53,14 @@ def get_all_algo(self): def check_engine(self): """ Check if the engine is supported. - For netowrkx, we need to convert the NebulaDataFrameObject to NebulaGraphObject + For netowrkx, we need to convert the NebulaDataFrameObject + to NebulaGraphObject For spark, we can directly use the NebulaDataFrameObject """ if self.ndf_obj.engine.type == "networkx": raise Exception( "For NebulaDataFrameObject in networkx engine," - "Please transform it to NebulaGraphObject to run algorithm", + "Plz transform it to NebulaGraphObject to run algorithm", "For example: g = nebula_df.to_graph; g.algo.pagerank()", ) @@ -73,7 +74,7 @@ def get_spark_engine_context(self, config_class: str, lib_class: str): jspark = engine.jspark engine.import_algo_config_class(config_class) engine.import_algo_lib_class(lib_class) - return engine, spark, jspark, engine.encode_vertex_id + return engine, spark, jspark, engine.encode_vid def get_spark_dataframe(self): """ @@ -93,22 +94,22 @@ def get_spark_dataframe(self): def pagerank( self, reset_prob: float = 0.15, max_iter: int = 10, weighted: bool = False ): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "PRConfig", "PageRankAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.PRConfig(max_iter, reset_prob, encode_vertex_id) + config = spark._jvm.PRConfig(max_iter, reset_prob, encode_vid) result = spark._jvm.PageRankAlgo.apply(jspark, df._jdf, config, weighted) return result @algo def connected_components(self, max_iter: int = 10, weighted: bool = False): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "CcConfig", "ConnectedComponentsAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.CcConfig(max_iter, encode_vertex_id) + config = spark._jvm.CcConfig(max_iter, encode_vid) result = spark._jvm.ConnectedComponentsAlgo.apply( jspark, df._jdf, config, weighted ) @@ -117,12 +118,12 @@ def connected_components(self, max_iter: int = 10, weighted: bool = False): @algo def label_propagation(self, max_iter: int = 10, weighted: bool = False): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "LPAConfig", "LabelPropagationAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.LPAConfig(max_iter, encode_vertex_id) + config = spark._jvm.LPAConfig(max_iter, encode_vid) result = spark._jvm.LabelPropagationAlgo.apply( jspark, df._jdf, config, weighted ) @@ -131,49 +132,50 @@ def label_propagation(self, max_iter: int = 10, weighted: bool = False): @algo def louvain(self, max_iter: int = 20, internalIter: int = 10, tol: float = 0.5): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "LouvainConfig", "LouvainAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.LouvainConfig(max_iter, internalIter, tol, encode_vertex_id) + config = spark._jvm.LouvainConfig(max_iter, internalIter, tol, encode_vid) result = spark._jvm.LouvainAlgo.apply(jspark, df._jdf, config, False) return result @algo def k_core(self, max_iter: int = 10, degree: int = 2): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "KCoreConfig", "KCoreAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.KCoreConfig(max_iter, degree, encode_vertex_id) + config = spark._jvm.KCoreConfig(max_iter, degree, encode_vid) result = spark._jvm.KCoreAlgo.apply(jspark, df._jdf, config) return result # def shortest_path(self, landmarks: list, weighted: bool = False): - # engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + # engine, spark, jspark, encode_vid = self.get_spark_engine_context( # "ShortestPathConfig", "ShortestPathAlgo" # ) # # TBD: ShortestPathAlgo is not yet encodeID compatible # df = self.get_spark_dataframe() - # config = spark._jvm.ShortestPathConfig(landmarks, encode_vertex_id) - # result = spark._jvm.ShortestPathAlgo.apply(jspark, df._jdf, config, weighted) + # config = spark._jvm.ShortestPathConfig(landmarks, encode_vid) + # result = spark._jvm.ShortestPathAlgo.apply( + # jspark, df._jdf, config, weighted) # return result @algo def degree_statics(self): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "DegreeStaticConfig", "DegreeStaticAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.DegreeStaticConfig(encode_vertex_id) + config = spark._jvm.DegreeStaticConfig(encode_vid) result = spark._jvm.DegreeStaticAlgo.apply(jspark, df._jdf, config) return result @@ -182,12 +184,12 @@ def degree_statics(self): def betweenness_centrality( self, max_iter: int = 10, degree: int = 2, weighted: bool = False ): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "BetweennessConfig", "BetweennessCentralityAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.BetweennessConfig(max_iter, encode_vertex_id) + config = spark._jvm.BetweennessConfig(max_iter, encode_vid) result = spark._jvm.BetweennessCentralityAlgo.apply( jspark, df._jdf, config, weighted ) @@ -201,24 +203,24 @@ def coefficient_centrality(self, type: str = "local"): "type should be either local or global" f"in coefficient_centrality algo. Got type: {type}" ) - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "CoefficientConfig", "ClusteringCoefficientAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.CoefficientConfig(type, encode_vertex_id) + config = spark._jvm.CoefficientConfig(type, encode_vid) result = spark._jvm.ClusteringCoefficientAlgo.apply(jspark, df._jdf, config) return result @algo def bfs(self, max_depth: int = 10, root: int = 1): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "BfsConfig", "BfsAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.BfsConfig(max_depth, root, encode_vertex_id) + config = spark._jvm.BfsConfig(max_depth, root, encode_vid) result = spark._jvm.BfsAlgo.apply(jspark, df._jdf, config) return result @@ -226,12 +228,12 @@ def bfs(self, max_depth: int = 10, root: int = 1): # dfs is not yet supported, need to revisit upstream nebula-algorithm @algo def dfs(self, max_depth: int = 10, root: int = 1): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "DfsConfig", "DfsAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.DfsConfig(max_depth, root, encode_vertex_id) + config = spark._jvm.DfsConfig(max_depth, root, encode_vid) result = spark._jvm.DfsAlgo.apply(jspark, df._jdf, config) return result @@ -245,13 +247,13 @@ def hanp( weighted: bool = False, preferences=None, ): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "HanpConfig", "HanpAlgo" ) df = self.get_spark_dataframe() config = spark._jvm.HanpConfig( - hop_attenuation, max_iter, preference, encode_vertex_id + hop_attenuation, max_iter, preference, encode_vid ) result = spark._jvm.HanpAlgo.apply( jspark, df._jdf, config, weighted, preferences @@ -278,7 +280,7 @@ def hanp( # model_path: str = "hdfs://127.0.0.1:9000/model", # weighted: bool = False, # ): - # engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + # engine, spark, jspark, encode_vid = self.get_spark_engine_context( # "Node2vecConfig", "Node2VecAlgo" # ) # # TBD: Node2VecAlgo is not yet encodeID compatible @@ -298,7 +300,7 @@ def hanp( # degree, # emb_separator, # model_path, - # encode_vertex_id, + # encode_vid, # ) # result = spark._jvm.Node2VecAlgo.apply(jspark, df._jdf, config, weighted) @@ -306,23 +308,25 @@ def hanp( @algo def jaccard(self, tol: float = 1.0): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "JaccardConfig", "JaccardAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.JaccardConfig(tol, encode_vertex_id) + config = spark._jvm.JaccardConfig(tol, encode_vid) result = spark._jvm.JaccardAlgo.apply(jspark, df._jdf, config) return result @algo - def strong_connected_components(self, max_iter: int = 10, weighted: bool = False): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + def strong_connected_components( + self, max_iter: int = 10, weighted: bool = False + ): + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "CcConfig", "StronglyConnectedComponentsAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.CcConfig(max_iter, encode_vertex_id) + config = spark._jvm.CcConfig(max_iter, encode_vid) result = spark._jvm.StronglyConnectedComponentsAlgo.apply( jspark, df._jdf, config, weighted ) @@ -331,11 +335,11 @@ def strong_connected_components(self, max_iter: int = 10, weighted: bool = False @algo def triangle_count(self): - engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + engine, spark, jspark, encode_vid = self.get_spark_engine_context( "TriangleConfig", "TriangleCountAlgo" ) df = self.get_spark_dataframe() - config = spark._jvm.TriangleConfig(encode_vertex_id) + config = spark._jvm.TriangleConfig(encode_vid) result = spark._jvm.TriangleCountAlgo.apply(jspark, df._jdf, config) return result @@ -343,12 +347,13 @@ def triangle_count(self): # @algo # def closeness(self, weighted: bool = False): # # TBD: ClosenessAlgo is not yet encodeID compatible - # engine, spark, jspark, encode_vertex_id = self.get_spark_engine_context( + # engine, spark, jspark, encode_vid = self.get_spark_engine_context( # "ClosenessConfig", "ClosenessAlgo" # ) # df = self.get_spark_dataframe() - # config = spark._jvm.ClosenessConfig(weighted, encode_vertex_id) - # result = spark._jvm.ClosenessAlgo.apply(jspark, df._jdf, config, False) + # config = spark._jvm.ClosenessConfig(weighted, encode_vid) + # result = spark._jvm.ClosenessAlgo.apply( + # jspark, df._jdf, config, False) # return result @@ -376,12 +381,13 @@ def check_engine(self): """ Check if the engine is supported. For netowrkx, we can directly call .algo.pagerank() - For spark, we need to convert the NebulaGraphObject to NebulaDataFrameObject + For spark, we need to convert the NebulaGraphObject + to NebulaDataFrameObject """ if self.graph.engine.type == "spark": raise Exception( "For NebulaGraphObject in spark engine," - "Please transform it to NebulaDataFrameObject to run algorithm", + "Plz transform it to NebulaDataFrameObject to run algorithm", "For example: df = nebula_graph.to_df; df.algo.pagerank()", ) diff --git a/ng_ai/nebula_data.py b/ng_ai/nebula_data.py index ec28dcc..1706878 100644 --- a/ng_ai/nebula_data.py +++ b/ng_ai/nebula_data.py @@ -114,7 +114,7 @@ def to_networkx(self): def to_graphx(self): if self.engine.type == "spark": - df = self.data + df = self.data # noqa: F841 # convert the df to a graphx graph, not implemented now raise NotImplementedError else: diff --git a/ng_ai/nebula_reader.py b/ng_ai/nebula_reader.py index d83797d..c3ec862 100644 --- a/ng_ai/nebula_reader.py +++ b/ng_ai/nebula_reader.py @@ -29,7 +29,10 @@ def show(self, **kwargs): class NebulaReader: def __init__( - self, engine="spark", config: NebulaGraphConfig = NebulaGraphConfig(), **kwargs + self, + engine="spark", + config: NebulaGraphConfig = NebulaGraphConfig(), + **kwargs, ): self.engine_type = engine if self.engine_type == "spark": diff --git a/ng_ai/nebula_writer.py b/ng_ai/nebula_writer.py index bd766ea..2cb4d4c 100644 --- a/ng_ai/nebula_writer.py +++ b/ng_ai/nebula_writer.py @@ -83,7 +83,8 @@ def __init__(self, data, sink: str, config: NebulaGraphConfig, **kwargs): def _convert_java_object_to_spark_df(self, jdf, column_map: dict = {}): """ Convert JavaObject to Spark DataFrame, with column name mapping - ref: https://stackoverflow.com/questions/36023860/how-to-use-a-scala-class-inside-pyspark + ref: https://stackoverflow.com/ + questions/36023860/how-to-use-a-scala-class-inside-pyspark """ from pyspark.sql import DataFrame @@ -132,7 +133,8 @@ def _set_options_with_nebula(self, **kwargs): writer.option("type", type) self._options["type"] = type - # space setting, set only when it's specified, else inherit from NebulaGraphConfig + # space setting, set only when it's specified, else inherit + # from NebulaGraphConfig space = kwargs.get("space", self.config.space) assert space is not None, "space should be specified" writer.option("spaceName", space) @@ -218,7 +220,7 @@ def _set_options_with_nebula(self, **kwargs): "", "hash", "uuid", - ], f"src_id_policy should be empty, hash or uuid, but got {src_id_policy}" + ], f"id_policy valid value: [empty, hash, uuid], got {src_id_policy}" writer.option("srcIdPolicy", src_id_policy) self._options["srcIdPolicy"] = src_id_policy @@ -228,7 +230,7 @@ def _set_options_with_nebula(self, **kwargs): "", "hash", "uuid", - ], f"dst_id_policy should be empty, hash or uuid, but got {dst_id_policy}" + ], f"id_policy valid value: [empty, hash, uuid], got {dst_id_policy}" writer.option("dstIdPolicy", dst_id_policy) self._options["dstIdPolicy"] = dst_id_policy @@ -256,8 +258,10 @@ def set_options(self, **kwargs): # cluster_id string NOT NULL # ); - louvain_result_df = louvain_result_df.withColumnRenamed("louvain", "cluster_id") - louvain_result_df.write.format("com.vesoft.nebula.connector.NebulaDataSource").option( + louvain_result_df = louvain_result_df.withColumnRenamed( + "louvain", "cluster_id") + louvain_result_df.write.format( + "com.vesoft.nebula.connector.NebulaDataSource").option( "type", "vertex").option( "spaceName", "basketballplayer").option( "label", "louvain").option( @@ -276,7 +280,8 @@ def set_options(self, **kwargs): self.raw_df = self._convert_java_object_to_spark_df( self.jdf, kwargs.get("properties", {}) ) - # TBD: check tag/edge type's schema against df's schema, raise error if not match + # TBD: check tag/edge type's schema against df's schema, + # raise error if not match self._get_raw_df_writer() # self.raw_df_writer diff --git a/ng_ai/ng_ai_api/__main__.py b/ng_ai/ng_ai_api/__main__.py index f32ffb1..6054de3 100644 --- a/ng_ai/ng_ai_api/__main__.py +++ b/ng_ai/ng_ai_api/__main__.py @@ -1,7 +1,7 @@ -from ng_ai import ng_ai_api_app as app - import os +from ng_ai import ng_ai_api_app as app + def run(): print(dir(app)) diff --git a/ng_ai/ng_ai_api/app.py b/ng_ai/ng_ai_api/app.py index 2345a25..3a3832c 100644 --- a/ng_ai/ng_ai_api/app.py +++ b/ng_ai/ng_ai_api/app.py @@ -1,10 +1,10 @@ import os +from flask import Flask, request + from ng_ai import NebulaReader, NebulaWriter from ng_ai.config import NebulaGraphConfig -from flask import Flask, request - app = Flask(__name__) @@ -37,7 +37,9 @@ def parallel(algo_name): # get algo_context algo_context = data.get("algo_context") assert algo_context is not None, "algo_context should not be None" - assert algo_context.get("space") is not None, "space should not be None" + assert ( + algo_context.get("space") is not None + ), "space should not be None" # noqa except Exception as e: print(e) return {"error": f"algo context parsing failed: {e}"} @@ -55,7 +57,7 @@ def parallel(algo_name): assert ( len(edges) == len(edge_weights) and len(edges) > 0 ), "edges and edge_weights should have the same length and length > 0" - # TBD, it seems that the reader.scan() need to support more than one edge type + # TBD, it seems the .scan() need to support more than one edge type # https://github.com/wey-gu/nebulagraph-ai/issues/19 # need to query all and union them. if read_mode == "scan": @@ -65,9 +67,16 @@ def parallel(algo_name): assert query is not None, "query should not be None" reader.query(query, edge=edges[0], props=edge_weights[0]) # TODO(wey): need to revisit the query and scan API, to align them. - # ref: https://github.com/vesoft-inc/nebula-algorithm/blob/master/nebula-algorithm/src/main/scala/com/vesoft/nebula/algorithm/Main.scala - # ref: https://github.com/vesoft-inc/nebula-algorithm/blob/master/nebula-algorithm/src/main/scala/com/vesoft/nebula/algorithm/reader/DataReader.scala - # ref: https://github.com/vesoft-inc/nebula-spark-connector/blob/master/example/src/main/scala/com/vesoft/nebula/examples/connector/NebulaSparkReaderExample.scala + # ref: https://github.com/vesoft-inc/nebula-algorithm/ + # blob/master/nebula-algorithm/src/main/scala/com/vesoft/nebula/algorithm/Main.scala + # + # ref: https://github.com/vesoft-inc/nebula-algorithm/ + # blob/master/nebula-algorithm/src/main/scala/com/vesoft/nebula/ + # algorithm/reader/DataReader.scala + # + # ref: https://github.com/vesoft-inc/nebula-spark-connector/ + # blob/master/example/src/main/scala/com/vesoft/nebula/examples/ + # connector/NebulaSparkReaderExample.scala df = reader.read() except Exception as e: # TBD, need to return error code, return empty json for now diff --git a/pdm.lock b/pdm.lock index 450d935..1c98446 100644 --- a/pdm.lock +++ b/pdm.lock @@ -2,9 +2,37 @@ # It is not intended for manual editing. [[package]] -name = "click" -version = "8.0.4" +name = "attrs" +version = "22.2.0" requires_python = ">=3.6" +summary = "Classes Without Boilerplate" + +[[package]] +name = "black" +version = "23.1.0" +requires_python = ">=3.7" +summary = "The uncompromising code formatter." +dependencies = [ + "click>=8.0.0", + "mypy-extensions>=0.4.3", + "packaging>=22.0", + "pathspec>=0.9.0", + "platformdirs>=2", + "tomli>=1.1.0; python_version < \"3.11\"", + "typed-ast>=1.4.2; python_version < \"3.8\" and implementation_name == \"cpython\"", + "typing-extensions>=3.10.0.0; python_version < \"3.10\"", +] + +[[package]] +name = "chispa" +version = "0.9.2" +requires_python = ">=3.5" +summary = "Pyspark test helper library" + +[[package]] +name = "click" +version = "8.1.3" +requires_python = ">=3.7" summary = "Composable command line interface toolkit" dependencies = [ "colorama; platform_system == \"Windows\"", @@ -13,37 +41,44 @@ dependencies = [ [[package]] name = "colorama" -version = "0.4.5" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Cross-platform colored terminal text." [[package]] -name = "dataclasses" -version = "0.8" -requires_python = ">=3.6, <3.7" -summary = "A backport of the dataclasses module for Python 3.6" +name = "exceptiongroup" +version = "1.1.1" +requires_python = ">=3.7" +summary = "Backport of PEP 654 (exception groups)" [[package]] -name = "decorator" -version = "4.4.2" -requires_python = ">=2.6, !=3.0.*, !=3.1.*" -summary = "Decorators for Humans" +name = "flake8" +version = "5.0.4" +requires_python = ">=3.6.1" +summary = "the modular source code checker: pep8 pyflakes and co" +dependencies = [ + "importlib-metadata<4.3,>=1.1.0; python_version < \"3.8\"", + "mccabe<0.8.0,>=0.7.0", + "pycodestyle<2.10.0,>=2.9.0", + "pyflakes<2.6.0,>=2.5.0", +] [[package]] name = "flask" -version = "2.0.3" -requires_python = ">=3.6" +version = "2.2.3" +requires_python = ">=3.7" summary = "A simple framework for building complex web applications." dependencies = [ "Jinja2>=3.0", - "Werkzeug>=2.0", - "click>=7.1.2", + "Werkzeug>=2.2.2", + "click>=8.0", + "importlib-metadata>=3.6.0; python_version < \"3.10\"", "itsdangerous>=2.0", ] [[package]] name = "importlib-metadata" -version = "4.8.3" +version = "4.2.0" requires_python = ">=3.6" summary = "Read metadata from Python packages" dependencies = [ @@ -51,16 +86,28 @@ dependencies = [ "zipp>=0.5", ] +[[package]] +name = "iniconfig" +version = "2.0.0" +requires_python = ">=3.7" +summary = "brain-dead simple config-ini parsing" + +[[package]] +name = "isort" +version = "5.11.5" +requires_python = ">=3.7.0" +summary = "A Python utility / library to sort Python imports." + [[package]] name = "itsdangerous" -version = "2.0.1" -requires_python = ">=3.6" +version = "2.1.2" +requires_python = ">=3.7" summary = "Safely pass data to untrusted environments and back." [[package]] name = "jinja2" -version = "3.0.3" -requires_python = ">=3.6" +version = "3.1.2" +requires_python = ">=3.7" summary = "A very fast and expressive template engine." dependencies = [ "MarkupSafe>=2.0", @@ -68,17 +115,56 @@ dependencies = [ [[package]] name = "markupsafe" -version = "2.0.1" -requires_python = ">=3.6" +version = "2.1.2" +requires_python = ">=3.7" summary = "Safely add untrusted strings to HTML/XML markup." [[package]] -name = "networkx" -version = "2.5.1" +name = "mccabe" +version = "0.7.0" requires_python = ">=3.6" +summary = "McCabe checker, plugin for flake8" + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +requires_python = ">=3.5" +summary = "Type system extensions for programs checked with the mypy type checker." + +[[package]] +name = "networkx" +version = "2.6.3" +requires_python = ">=3.7" summary = "Python package for creating and manipulating graphs and networks" + +[[package]] +name = "packaging" +version = "23.0" +requires_python = ">=3.7" +summary = "Core utilities for Python packages" + +[[package]] +name = "pathspec" +version = "0.11.1" +requires_python = ">=3.7" +summary = "Utility library for gitignore style pattern matching of file paths." + +[[package]] +name = "platformdirs" +version = "3.1.1" +requires_python = ">=3.7" +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +dependencies = [ + "typing-extensions>=4.4; python_version < \"3.8\"", +] + +[[package]] +name = "pluggy" +version = "1.0.0" +requires_python = ">=3.6" +summary = "plugin and hook calling mechanisms for python" dependencies = [ - "decorator<5,>=4.3", + "importlib-metadata>=0.12; python_version < \"3.8\"", ] [[package]] @@ -87,163 +173,293 @@ version = "0.10.9.5" summary = "Enables Python programs to dynamically access arbitrary Java objects" [[package]] -name = "pyspark" -version = "3.2.3" +name = "pycodestyle" +version = "2.9.1" +requires_python = ">=3.6" +summary = "Python style guide checker" + +[[package]] +name = "pyflakes" +version = "2.5.0" requires_python = ">=3.6" +summary = "passive checker of Python programs" + +[[package]] +name = "pyspark" +version = "3.3.2" +requires_python = ">=3.7" summary = "Apache Spark Python API" dependencies = [ "py4j==0.10.9.5", ] [[package]] -name = "typing-extensions" -version = "4.1.1" +name = "pytest" +version = "7.2.2" +requires_python = ">=3.7" +summary = "pytest: simple powerful testing with Python" +dependencies = [ + "attrs>=19.2.0", + "colorama; sys_platform == \"win32\"", + "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", + "importlib-metadata>=0.12; python_version < \"3.8\"", + "iniconfig", + "packaging", + "pluggy<2.0,>=0.12", + "tomli>=1.0.0; python_version < \"3.11\"", +] + +[[package]] +name = "tomli" +version = "2.0.1" +requires_python = ">=3.7" +summary = "A lil' TOML parser" + +[[package]] +name = "typed-ast" +version = "1.5.4" requires_python = ">=3.6" -summary = "Backported and Experimental Type Hints for Python 3.6+" +summary = "a fork of Python 2 and 3 ast modules with type comment support" + +[[package]] +name = "typing-extensions" +version = "4.5.0" +requires_python = ">=3.7" +summary = "Backported and Experimental Type Hints for Python 3.7+" [[package]] name = "werkzeug" -version = "2.0.3" -requires_python = ">=3.6" +version = "2.2.3" +requires_python = ">=3.7" summary = "The comprehensive WSGI web application library." dependencies = [ - "dataclasses; python_version < \"3.7\"", + "MarkupSafe>=2.1.1", ] [[package]] name = "zipp" -version = "3.6.0" -requires_python = ">=3.6" +version = "3.15.0" +requires_python = ">=3.7" summary = "Backport of pathlib-compatible object wrapper for zip files" [metadata] lock_version = "4.1" -content_hash = "sha256:697253d60319a635a2e51e4de909c85ec5b3be273aad73f4ad75b0e36d1616bf" +content_hash = "sha256:744e451e5442eda41d35ca976c8ea011eb0f1aae04f1c23af3847063d95f7b9b" [metadata.files] -"click 8.0.4" = [ - {url = "https://files.pythonhosted.org/packages/4a/a8/0b2ced25639fb20cc1c9784de90a8c25f9504a7f18cd8b5397bd61696d7d/click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, - {url = "https://files.pythonhosted.org/packages/dd/cf/706c1ad49ab26abed0b77a2f867984c1341ed7387b8030a6aa914e2942a0/click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, -] -"colorama 0.4.5" = [ - {url = "https://files.pythonhosted.org/packages/2b/65/24d033a9325ce42ccbfa3ca2d0866c7e89cc68e5b9d92ecaba9feef631df/colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, - {url = "https://files.pythonhosted.org/packages/77/8b/7550e87b2d308a1b711725dfaddc19c695f8c5fa413c640b2be01662f4e6/colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, -] -"dataclasses 0.8" = [ - {url = "https://files.pythonhosted.org/packages/1f/12/7919c5d8b9c497f9180db15ea8ead6499812ea8264a6ae18766d93c59fe5/dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, - {url = "https://files.pythonhosted.org/packages/fe/ca/75fac5856ab5cfa51bbbcefa250182e50441074fdc3f803f6e76451fab43/dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, -] -"decorator 4.4.2" = [ - {url = "https://files.pythonhosted.org/packages/da/93/84fa12f2dc341f8cf5f022ee09e109961055749df2d0c75c5f98746cfe6c/decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, - {url = "https://files.pythonhosted.org/packages/ed/1b/72a1821152d07cf1d8b6fce298aeb06a7eb90f4d6d41acec9861e7cc6df0/decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, -] -"flask 2.0.3" = [ - {url = "https://files.pythonhosted.org/packages/84/9d/66347e6b3e2eb78647392d3969c23bdc2d8b2fdc32bd078c817c15cb81ad/Flask-2.0.3.tar.gz", hash = "sha256:e1120c228ca2f553b470df4a5fa927ab66258467526069981b3eb0a91902687d"}, - {url = "https://files.pythonhosted.org/packages/cd/77/59df23681f4fd19b7cbbb5e92484d46ad587554f5d490f33ef907e456132/Flask-2.0.3-py3-none-any.whl", hash = "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f"}, -] -"importlib-metadata 4.8.3" = [ - {url = "https://files.pythonhosted.org/packages/85/ed/e65128cc5cb1580f22ee3009d9187ecdfcc43ffb3b581fe854b24e87d8e7/importlib_metadata-4.8.3.tar.gz", hash = "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"}, - {url = "https://files.pythonhosted.org/packages/a0/a1/b153a0a4caf7a7e3f15c2cd56c7702e2cf3d89b1b359d1f1c5e59d68f4ce/importlib_metadata-4.8.3-py3-none-any.whl", hash = "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e"}, -] -"itsdangerous 2.0.1" = [ - {url = "https://files.pythonhosted.org/packages/58/66/d6c5859dcac92b442626427a8c7a42322068c5cd5d4a463ce78b93f730b7/itsdangerous-2.0.1.tar.gz", hash = "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"}, - {url = "https://files.pythonhosted.org/packages/9c/96/26f935afba9cd6140216da5add223a0c465b99d0f112b68a4ca426441019/itsdangerous-2.0.1-py3-none-any.whl", hash = "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c"}, -] -"jinja2 3.0.3" = [ - {url = "https://files.pythonhosted.org/packages/20/9a/e5d9ec41927401e41aea8af6d16e78b5e612bca4699d417f646a9610a076/Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, - {url = "https://files.pythonhosted.org/packages/91/a5/429efc6246119e1e3fbf562c00187d04e83e54619249eb732bb423efa6c6/Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, -] -"markupsafe 2.0.1" = [ - {url = "https://files.pythonhosted.org/packages/04/69/c31e837e4bb5532b02d297152464b2cb8a0edeb9bef762c015e9b4e95e16/MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, - {url = "https://files.pythonhosted.org/packages/08/dc/a5ed54fcc61f75343663ee702cbf69831dcec9b1a952ae21cf3d1fbc56ba/MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, - {url = "https://files.pythonhosted.org/packages/09/f1/5ca5da61ec071ce1e9c423f66a5bde508957601118be9cd37aeccfeab2f6/MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, - {url = "https://files.pythonhosted.org/packages/0a/1d/12eb0e1d1d7e0f745cd7bcf27400d75b53096ae14f9b86d3be02a468bc75/MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, - {url = "https://files.pythonhosted.org/packages/0c/55/d7b9059ed9affe3ebdaa288006e4b82839bdbc0ecf092cd5b61d0f0ba456/MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, - {url = "https://files.pythonhosted.org/packages/15/90/b63743e72c9ffc5988c7b1c04d14f9a32ae49574afe8a7fbea0ce538bda4/MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, - {url = "https://files.pythonhosted.org/packages/1b/f6/f774e745edd76eef70bf83062823be0dca95ee9c9211f18aec490892ab33/MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, - {url = "https://files.pythonhosted.org/packages/1d/c5/1d1b42c65f96ee7b0c81761260878d1a1dc0afdf259e434b7d7af88a80a3/MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, - {url = "https://files.pythonhosted.org/packages/1f/44/ada8e01854175525e8e139278c3a52fec0ef720307cbd670bca86b473b56/MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, - {url = "https://files.pythonhosted.org/packages/20/0e/e5d5ed4bad48827aede890787b8855c7dc08301be60f2eeb0ce17ec5c810/MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, - {url = "https://files.pythonhosted.org/packages/21/84/e090d999105fe0f3e1d955725ed2c9aeebc649ee83edab0e73d353d47e5d/MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, - {url = "https://files.pythonhosted.org/packages/27/c3/20f02d95e78756d59a4c02f179a6ee66e3283cc34e3051d436fd152d1e76/MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, - {url = "https://files.pythonhosted.org/packages/2b/6b/69dd812a582de48190e73c08a4f526842f880a4bb53fbc6859d896621b54/MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, - {url = "https://files.pythonhosted.org/packages/30/9e/4b7116f464a0151b86ce42b5185941eb74c207b38fe033f71f5e5d150356/MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, - {url = "https://files.pythonhosted.org/packages/35/3e/30d8e0f71de72ccb813ba82191dc445b6f2d8aaa08169a3814384e6f39c3/MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, - {url = "https://files.pythonhosted.org/packages/3b/41/f53e2ac439b309d8bb017d12ee6e7d393aa70c508448c1f30a7e5db9d69e/MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, - {url = "https://files.pythonhosted.org/packages/3f/43/72fd80844b2687e2c5aac95b64662ede122b8c3919b4c95488017ca8d2a9/MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, - {url = "https://files.pythonhosted.org/packages/44/e6/4e1f202ec01062c8b4d03af72f1aeb2ca8fc97f9f5d95b9173302ac4e5ad/MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, - {url = "https://files.pythonhosted.org/packages/50/99/06eccf68be0bff67ab9a0b90b5382c04769f9ad2e42cae5e5e92f99380cd/MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, - {url = "https://files.pythonhosted.org/packages/51/1e/45e25cd867fb79339c49086dad9794e11923dd6325251ae48c341b0a4271/MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, - {url = "https://files.pythonhosted.org/packages/51/c3/7154db2b7d5b24875e1f1c42bab87a46af688bd6a5c89a90c60053cb6b33/MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, - {url = "https://files.pythonhosted.org/packages/53/e8/601efa63c4058311a8bda7984a2fe554b9da574044967d7aee253661ee46/MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, - {url = "https://files.pythonhosted.org/packages/5a/98/3303496a5d19aabba67c443ba1df6ee1bec94549b3f8976f90c06a6942e6/MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, - {url = "https://files.pythonhosted.org/packages/5a/ff/34bdcd8cc794f692588de0b3f4c1aa7ec0d17716fda9d874836ed68775c1/MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, - {url = "https://files.pythonhosted.org/packages/5b/db/49785acd523bd5eef83d0e21594eec1c2d7d45afc473dcc85037243de673/MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, - {url = "https://files.pythonhosted.org/packages/66/66/b5891704372c9f5d97432933bdd7e9b5a0647fad9170c72bb7f486550c43/MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, - {url = "https://files.pythonhosted.org/packages/67/e9/579a3ad8d48f7680f887ff1f22cc6330f083de23ce32a8fa35f8acef477a/MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, - {url = "https://files.pythonhosted.org/packages/68/ba/7a5ca0f9b4239e6fd846dd54c0b5928187355fa62fbdbd13e1c5942afae7/MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, - {url = "https://files.pythonhosted.org/packages/6a/96/7a23b44f742384a866173502e19cc1ec13951085bbb4e24be504dfc6da9f/MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, - {url = "https://files.pythonhosted.org/packages/6f/83/eabfb8c6d60b096dc9ada378cf935809289c4d0327b74a60789bde77e1db/MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, - {url = "https://files.pythonhosted.org/packages/70/56/f81c0cfbc22882df36358ecdedc5474571183e5a5adde1e237079acee437/MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, - {url = "https://files.pythonhosted.org/packages/70/fc/5a7253a9c1c4e2a3feadb80a5def4563500daa4b2d4a39cae39483afa1b0/MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, - {url = "https://files.pythonhosted.org/packages/73/60/296031f365b3ae96732225203d864fac7b83a185ed1820c1c87b78e154bc/MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, - {url = "https://files.pythonhosted.org/packages/74/5d/3d5d08321661ca30c61eb897cd9fdf35a9a63ddddd094e65deb9862986b7/MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, - {url = "https://files.pythonhosted.org/packages/75/90/b780381ddf38e2afd07a04746b5d3158a085464f7c757fc62cd198aa5379/MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, - {url = "https://files.pythonhosted.org/packages/7a/e8/00c435416c9b0238dca6f883563b01c4cc532b2ba6aaf7268081f6238520/MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, - {url = "https://files.pythonhosted.org/packages/80/ec/e4272ac306ccc17062d253cb11f5c79c457f5e78b0e3c9f6adc989d507c0/MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, - {url = "https://files.pythonhosted.org/packages/81/8b/f28eac2790d49dde61f89ae9e007ac65002edc90bb2dd63c3b9e653820d2/MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, - {url = "https://files.pythonhosted.org/packages/8f/87/4668ce3963e942a9aa7b13212158e74bf063a2461138b7ed5a043ac6aa79/MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, - {url = "https://files.pythonhosted.org/packages/92/ac/94771b65ac9f77cf37e43b38516697bbc4e128ee152b68d596ae44c6c896/MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, - {url = "https://files.pythonhosted.org/packages/93/28/d42b954fb9189cf4b78b0b0a025cff9b2583f93b37d1a345768ade29e5dd/MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, - {url = "https://files.pythonhosted.org/packages/95/18/b7a45c16635acafdf6837a6fd4c71acfe5bad202884c6fcbae4ea0763dde/MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, - {url = "https://files.pythonhosted.org/packages/9c/dd/1b57e1514fd2f653ee31255b940baf0609741bc059565a7fe7c4e0fec46d/MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, - {url = "https://files.pythonhosted.org/packages/a3/01/8d5fd91ccc1a61b7a9e2803819b8b60c3bac37290bbbd3df33d8d548f9c1/MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, - {url = "https://files.pythonhosted.org/packages/a4/c8/9d2161b2080cb69c8834d1c34a399685347523acbfc923b203ad27bf1215/MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, - {url = "https://files.pythonhosted.org/packages/a6/d1/a7b97d0e000336c4e06bfce7e08dcb2b47fc5091146ee883dfac6cb4842e/MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, - {url = "https://files.pythonhosted.org/packages/a7/55/a576835b6b95af21d15f69eaf14c4fb1358fd48475f2b9813abd9654132e/MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, - {url = "https://files.pythonhosted.org/packages/ad/cd/650b1be2a81674939ef962b1f1b956e4a84116d69708c842667445e95408/MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, - {url = "https://files.pythonhosted.org/packages/ae/70/8dd5f2c0aab82431c9c619a2c4fbd1742fc0fb769d8d7b275ae1d03eb3a5/MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, - {url = "https://files.pythonhosted.org/packages/b9/87/cdfd4778d4b9ef0dc89c62b3cf0c181c9231e523a90d7ee254afcfe74557/MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, - {url = "https://files.pythonhosted.org/packages/bf/10/ff66fea6d1788c458663a84d88787bae15d45daa16f6b3ef33322a51fc7e/MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, - {url = "https://files.pythonhosted.org/packages/bf/a8/76f613645617c31dd4db1950057b0bab68e0b790c2dbb368c1971d38d87e/MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, - {url = "https://files.pythonhosted.org/packages/c1/39/9df65c006a88fce7bbd5ec3195b949b79477b1a325564f486c611c367893/MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, - {url = "https://files.pythonhosted.org/packages/c2/db/314df69668f582d5173922bded7b58126044bb77cfce6347c5d992074d2e/MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, - {url = "https://files.pythonhosted.org/packages/cc/f2/854d33eee85df681e61e22b52d8e83bef8b7425c0b9826212289f7885710/MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, - {url = "https://files.pythonhosted.org/packages/ce/a7/835a636047f4bb4fea31a682c18affad9795e864d800892bd7248485425e/MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, - {url = "https://files.pythonhosted.org/packages/d7/56/9d9c0dc2b0f5dc342ff9c7df31c523cc122947970b5ea943b2311be0c391/MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, - {url = "https://files.pythonhosted.org/packages/dd/8f/d0c570c851f70377ca6f344531fab4b6b01a99a9d2a801b25d6fd75525e5/MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, - {url = "https://files.pythonhosted.org/packages/e2/a9/eafee9babd4b3aed918d286fbe1c20d1a22d347b30d2bddb3c49919548fa/MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, - {url = "https://files.pythonhosted.org/packages/e4/9b/c7b55a2f587368d69eb6dc36e285010ab0bbb74323833d501921e08e2728/MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, - {url = "https://files.pythonhosted.org/packages/e6/57/e9d243b12918f22bc3aa1392db7821dcb643a120e87b3f8c9bc7e1ad33f1/MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, - {url = "https://files.pythonhosted.org/packages/e9/b8/e0e089d26667fbac3a473f78fc771b1cbffd30964816928e4864aac43357/MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, - {url = "https://files.pythonhosted.org/packages/eb/3b/1cddaf0338a031ef5c2e1d9d74f2d607d564748a933b44de6edfe7a2a880/MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, - {url = "https://files.pythonhosted.org/packages/ee/d4/f6d8700729ca202fd070e03d08bda349bb0689514c11732dcb4f0e7bd60f/MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, - {url = "https://files.pythonhosted.org/packages/f5/ff/9a35fc0f3fbda4cada0e559833b84627ddf44c45664741aed7da1b2468f2/MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, - {url = "https://files.pythonhosted.org/packages/f9/12/b63afcb3bf9f27fd347adef452f9a6e27dfe7107a8f2685afacc8e9c6592/MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, - {url = "https://files.pythonhosted.org/packages/fa/7f/50e0b7a7c13e056f7f1ea799a04a64c225a7ae784785f6b74e7515ea94e8/MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, - {url = "https://files.pythonhosted.org/packages/fc/d6/57f9a97e56447a1e340f8574836d3b636e2c14de304943836bd645fa9c7e/MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, - {url = "https://files.pythonhosted.org/packages/ff/e2/bfd4e230d609fc7c59cc1a69e1b9f65bda3f05b8cab41bb4437f3d44b108/MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, -] -"networkx 2.5.1" = [ - {url = "https://files.pythonhosted.org/packages/b0/21/adfbf6168631e28577e4af9eb9f26d75fe72b2bb1d33762a5f2c425e6c2a/networkx-2.5.1.tar.gz", hash = "sha256:109cd585cac41297f71103c3c42ac6ef7379f29788eb54cb751be5a663bb235a"}, - {url = "https://files.pythonhosted.org/packages/f3/b7/c7f488101c0bb5e4178f3cde416004280fd40262433496830de8a8c21613/networkx-2.5.1-py3-none-any.whl", hash = "sha256:0635858ed7e989f4c574c2328380b452df892ae85084144c73d8cd819f0c4e06"}, +"attrs 22.2.0" = [ + {url = "https://files.pythonhosted.org/packages/21/31/3f468da74c7de4fcf9b25591e682856389b3400b4b62f201e65f15ea3e07/attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, + {url = "https://files.pythonhosted.org/packages/fb/6e/6f83bf616d2becdf333a1640f1d463fef3150e2e926b7010cb0f81c95e88/attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, +] +"black 23.1.0" = [ + {url = "https://files.pythonhosted.org/packages/01/8a/065d0a59c1ebe13186b12a2fa3965a41fc1588828709995e2630004d216e/black-23.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8178318cb74f98bc571eef19068f6ab5613b3e59d4f47771582f04e175570ed8"}, + {url = "https://files.pythonhosted.org/packages/15/11/533355217b1cc4a6df3263048060c1527f733d4720e158de2085293112bb/black-23.1.0.tar.gz", hash = "sha256:b0bd97bea8903f5a2ba7219257a44e3f1f9d00073d6cc1add68f0beec69692ac"}, + {url = "https://files.pythonhosted.org/packages/18/99/bb1be0ff3a7e912679ad234a3c4884fa7689dfcc4eae85bddb6c04feaa62/black-23.1.0-py3-none-any.whl", hash = "sha256:7a0f701d314cfa0896b9001df70a530eb2472babb76086344e688829efd97d32"}, + {url = "https://files.pythonhosted.org/packages/20/de/eff8e3ccc22b5c2be1265a9e61f1006d03e194519a3ca2e83dd8483dbbb5/black-23.1.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:49f7b39e30f326a34b5c9a4213213a6b221d7ae9d58ec70df1c4a307cf2a1580"}, + {url = "https://files.pythonhosted.org/packages/2d/9a/a81bf384a08d8a5e13d97223a60a74ac3c16c0aecdbd85edbc662d158bde/black-23.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9afd3f493666a0cd8f8df9a0200c6359ac53940cbde049dcb1a7eb6ee2dd7074"}, + {url = "https://files.pythonhosted.org/packages/32/a7/1d207427b87780c505a41c9430d26362e729954503b8ffba27c4f53a6810/black-23.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bf649fda611c8550ca9d7592b69f0637218c2369b7744694c5e4902873b2f3a"}, + {url = "https://files.pythonhosted.org/packages/3d/dc/12dc29bb38b8db68c79b8339de1590fe1ae796858bfa6cf7494eb672be21/black-23.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b70eb40a78dfac24842458476135f9b99ab952dd3f2dab738c1881a9b38b753"}, + {url = "https://files.pythonhosted.org/packages/3e/c0/abc7031d670d211e4e2a063910d587dfcb62ce469631e779b23b66653442/black-23.1.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:57c18c5165c1dbe291d5306e53fb3988122890e57bd9b3dcb75f967f13411a26"}, + {url = "https://files.pythonhosted.org/packages/43/bc/5232fd6b0fd6d6177140cfb7d8f0f0e06638e2a750122767e265beb91161/black-23.1.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:a29650759a6a0944e7cca036674655c2f0f63806ddecc45ed40b7b8aa314b651"}, + {url = "https://files.pythonhosted.org/packages/6b/d1/4394e4b0a24ad0f556aca3ab11e27f2e199f03b43f147c31a4befbf62b48/black-23.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:121ca7f10b4a01fd99951234abdbd97728e1240be89fde18480ffac16503d481"}, + {url = "https://files.pythonhosted.org/packages/77/11/db2ae5bf93af5185086d9b1baf4ce369ca34c3a01835230873aa9163d52d/black-23.1.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:bb460c8561c8c1bec7824ecbc3ce085eb50005883a6203dcfb0122e95797ee06"}, + {url = "https://files.pythonhosted.org/packages/7e/fe/6c05c3f9255b7b498cfb88faa85b45329f1b7b0ecb444ebdc6b74ffa1457/black-23.1.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c1c476bc7b7d021321e7d93dc2cbd78ce103b84d5a4cf97ed535fbc0d6660648"}, + {url = "https://files.pythonhosted.org/packages/96/af/3361b34907efbfd9d55af453488be2282f831d98b7d201248b38d4c44346/black-23.1.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:a8471939da5e824b891b25751955be52ee7f8a30a916d570a5ba8e0f2eb2ecad"}, + {url = "https://files.pythonhosted.org/packages/9a/ee/549e8be7f635cabcc3c7c3f2c3b27971dc32735155631b9ef2dcb1bd861f/black-23.1.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:bfffba28dc52a58f04492181392ee380e95262af14ee01d4bc7bb1b1c6ca8d27"}, + {url = "https://files.pythonhosted.org/packages/a4/ec/934e89820289e6952922fa5965aec0e046ed65da168ffb0515af1e3364e1/black-23.1.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:a59db0a2094d2259c554676403fa2fac3473ccf1354c1c63eccf7ae65aac8ab6"}, + {url = "https://files.pythonhosted.org/packages/ae/93/1e62fe94ab531bdc3f6cbbbd5b518727277bf40f695777b4097db5da2a38/black-23.1.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c91dfc2c2a4e50df0026f88d2215e166616e0c80e86004d0003ece0488db2739"}, + {url = "https://files.pythonhosted.org/packages/b1/7e/c368e9c795387a01bc181d8acbfd178278cc9960c5e7ef1059222a4419f9/black-23.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a436e7881d33acaf2536c46a454bb964a50eff59b21b51c6ccf5a40601fbef24"}, + {url = "https://files.pythonhosted.org/packages/b7/33/8e074fd8b86a1c8668f5493ed28929d87bdccb6aa68c2975b47a02f92287/black-23.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a951cc83ab535d248c89f300eccbd625e80ab880fbcfb5ac8afb5f01a258ac9"}, + {url = "https://files.pythonhosted.org/packages/be/f9/11e401323cd5b4e53d138fc880564765466a86acd2d4b50d7c8cdd048c18/black-23.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6663f91b6feca5d06f2ccd49a10f254f9298cc1f7f49c46e498a0771b507104"}, + {url = "https://files.pythonhosted.org/packages/c0/1d/8dac412cf5cc4120a438969a4fafefdc3de8fa13d411f317a9f9f1e268a4/black-23.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0680d4380db3719ebcfb2613f34e86c8e6d15ffeabcf8ec59355c5e7b85bb555"}, + {url = "https://files.pythonhosted.org/packages/cf/fe/dda4b7eedb9d4dc46e306b814f7838cd9026907fdc889f75eb9f6d47d414/black-23.1.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:9880d7d419bb7e709b37e28deb5e68a49227713b623c72b2b931028ea65f619b"}, + {url = "https://files.pythonhosted.org/packages/d0/cb/0a38ffdafbb4b3f337adaf1b79aeaf4b8a21ed18835acad6349e46c78c80/black-23.1.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:b6a92a41ee34b883b359998f0c8e6eb8e99803aa8bf3123bf2b2e6fec505a221"}, + {url = "https://files.pythonhosted.org/packages/dd/19/875b5006e40ea69a4120671f50317100b24732f2b877203722c91bc4eef3/black-23.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:162e37d49e93bd6eb6f1afc3e17a3d23a823042530c37c3c42eeeaf026f38468"}, + {url = "https://files.pythonhosted.org/packages/e6/0a/9a5fca4a2ca07d4dbc3b00445c9353f05ea182b000f68c9ad6ba1da87a47/black-23.1.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:0052dba51dec07ed029ed61b18183942043e00008ec65d5028814afaab9a22fd"}, + {url = "https://files.pythonhosted.org/packages/f1/89/ccc28cb74a66c094b609295b009b5e0350c10b75661d2450eeed2f60ce37/black-23.1.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:382998821f58e5c8238d3166c492139573325287820963d2f7de4d518bd76958"}, +] +"chispa 0.9.2" = [ + {url = "https://files.pythonhosted.org/packages/03/8a/060df449d0a53a9c8480ccadfbcbba000a009d6e357f183a88adbe23f458/chispa-0.9.2.tar.gz", hash = "sha256:621ad2e64fd27e7372c7b90ab2d5ad1f8dd69b737a3421ba5b6f84b113a18b84"}, + {url = "https://files.pythonhosted.org/packages/51/b3/c7af5ae69e59b545ed6fcfe7f606e32a0efad36ba21a15b393ae69015ffd/chispa-0.9.2-py3-none-any.whl", hash = "sha256:c6eae922f5c3ccd08f4dc3707202291bb249e68e319d0641795d92d80cfb1cad"}, +] +"click 8.1.3" = [ + {url = "https://files.pythonhosted.org/packages/59/87/84326af34517fca8c58418d148f2403df25303e02736832403587318e9e8/click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, + {url = "https://files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, +] +"colorama 0.4.6" = [ + {url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +"exceptiongroup 1.1.1" = [ + {url = "https://files.pythonhosted.org/packages/61/97/17ed81b7a8d24d8f69b62c0db37abbd8c0042d4b3fc429c73dab986e7483/exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {url = "https://files.pythonhosted.org/packages/cc/38/57f14ddc8e8baeddd8993a36fe57ce7b4ba174c35048b9a6d270bb01e833/exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] +"flake8 5.0.4" = [ + {url = "https://files.pythonhosted.org/packages/ad/00/9808c62b2d529cefc69ce4e4a1ea42c0f855effa55817b7327ec5b75e60a/flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, + {url = "https://files.pythonhosted.org/packages/cf/a0/b881b63a17a59d9d07f5c0cc91a29182c8e8a9aa2bde5b3b2b16519c02f4/flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, +] +"flask 2.2.3" = [ + {url = "https://files.pythonhosted.org/packages/95/9c/a3542594ce4973786236a1b7b702b8ca81dbf40ea270f0f96284f0c27348/Flask-2.2.3-py3-none-any.whl", hash = "sha256:c0bec9477df1cb867e5a67c9e1ab758de9cb4a3e52dd70681f59fa40a62b3f2d"}, + {url = "https://files.pythonhosted.org/packages/e8/5c/ff9047989bd995b1098d14b03013f160225db2282925b517bb4a967752ee/Flask-2.2.3.tar.gz", hash = "sha256:7eb373984bf1c770023fce9db164ed0c3353cd0b53f130f4693da0ca756a2e6d"}, +] +"importlib-metadata 4.2.0" = [ + {url = "https://files.pythonhosted.org/packages/22/51/52442c59db26637681148c21f8984eed58c9db67053a0a4783a047010c98/importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, + {url = "https://files.pythonhosted.org/packages/c7/7c/126a8686399ebe256b5e4343ea80b6f2ee91549969da2eef0bb2891b8d24/importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, +] +"iniconfig 2.0.0" = [ + {url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, + {url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, +] +"isort 5.11.5" = [ + {url = "https://files.pythonhosted.org/packages/5f/f6/c55db45970fbd14de6ab72082f1b8a143c3a69aa031c1e0dd4b9ecc8d496/isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, + {url = "https://files.pythonhosted.org/packages/67/63/18cc5c2f9084d3f91ce704f2b5c8e17bedd777244e7732c21a31992b0a78/isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, +] +"itsdangerous 2.1.2" = [ + {url = "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {url = "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] +"jinja2 3.1.2" = [ + {url = "https://files.pythonhosted.org/packages/7a/ff/75c28576a1d900e87eb6335b063fab47a8ef3c8b4d88524c4bf78f670cce/Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {url = "https://files.pythonhosted.org/packages/bc/c3/f068337a370801f372f2f8f6bad74a5c140f6fda3d9de154052708dd3c65/Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, +] +"markupsafe 2.1.2" = [ + {url = "https://files.pythonhosted.org/packages/02/2c/18d55e5df6a9ea33709d6c33e08cb2e07d39e20ad05d8c6fbf9c9bcafd54/MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {url = "https://files.pythonhosted.org/packages/04/cf/9464c3c41b7cdb8df660cda75676697e7fb49ce1be7691a1162fc88da078/MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {url = "https://files.pythonhosted.org/packages/06/3b/d026c21cd1dbee89f41127e93113dcf5fa85c6660d108847760b59b3a66d/MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {url = "https://files.pythonhosted.org/packages/0a/88/78cb3d95afebd183d8b04442685ab4c70cfc1138b850ba20e2a07aff2f53/MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {url = "https://files.pythonhosted.org/packages/0d/15/82b108c697bec4c26c00aed6975b778cf0eac6cbb77598862b10550b7fcc/MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {url = "https://files.pythonhosted.org/packages/19/00/3b8eb0093c885576a1ce7f2263e7b8c01e55b9977433f8246f57cd81b0be/MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {url = "https://files.pythonhosted.org/packages/1f/20/76f6337f1e7238a626ab34405ddd634636011b2ff947dcbd8995f16a7776/MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {url = "https://files.pythonhosted.org/packages/22/88/9c0cae2f5ada778182f2842b377dd273d6be689953345c10b165478831eb/MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {url = "https://files.pythonhosted.org/packages/29/d2/243e6b860d97c18d848fc2bee2f39d102755a2b04a5ce4d018d839711b46/MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {url = "https://files.pythonhosted.org/packages/30/3e/0a69a24adb38df83e2f6989c38d68627a5f27181c82ecaa1fd03d1236dca/MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {url = "https://files.pythonhosted.org/packages/34/19/64b0abc021b22766e86efee32b0e2af684c4b731ce8ac1d519c791800c13/MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {url = "https://files.pythonhosted.org/packages/37/b2/6f4d5cac75ba6fe9f17671304fe339ea45a73c5609b5f5e652aa79c915c8/MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {url = "https://files.pythonhosted.org/packages/39/8d/5c5ce72deb8567ab48a18fbd99dc0af3dd651b6691b8570947e54a28e0f3/MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {url = "https://files.pythonhosted.org/packages/3d/66/2f636ba803fd6eb4cee7b3106ae02538d1e84a7fb7f4f8775c6528a87d31/MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {url = "https://files.pythonhosted.org/packages/41/54/6e88795c64ab5dcda31b06406c062c2740d1a64db18219d4e21fc90928c1/MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {url = "https://files.pythonhosted.org/packages/46/0c/10ee33673c5e36fa3809cf136971f81d951ca38516188ee11a965d9b2fe9/MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {url = "https://files.pythonhosted.org/packages/48/cc/d027612e17b56088cfccd2c8e083518995fcb25a7b4f17fc146362a0499d/MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {url = "https://files.pythonhosted.org/packages/4b/34/dc837e5ad9e14634aac4342eb8b12a9be20a4f74f50cc0d765f7aa2fc1e3/MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {url = "https://files.pythonhosted.org/packages/50/41/1442b693a40eb76d835ca2016e86a01479f17d7fd8288f9830f6790e366a/MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {url = "https://files.pythonhosted.org/packages/52/36/b35c577c884ea352fc0c1eaed9ca4946ffc22cc9c3527a70408bfa9e9496/MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {url = "https://files.pythonhosted.org/packages/56/0d/c9818629672a3368b773fa94597d79da77bdacc3186f097bb85023f785f6/MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {url = "https://files.pythonhosted.org/packages/5a/94/d056bf5dbadf7f4b193ee2a132b3d49ffa1602371e3847518b2982045425/MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {url = "https://files.pythonhosted.org/packages/5e/f6/8eb8a5692c1986b6e863877b0b8a83628aff14e5fbfaf11d9522b532bd9d/MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {url = "https://files.pythonhosted.org/packages/66/21/dadb671aade8eb67ef96e0d8f90b1bd5e8cfb6ad9d8c7fa2c870ec0c257b/MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {url = "https://files.pythonhosted.org/packages/76/b5/05ce70a3e31ecebcd3628cd180dc4761293aa496db85170fdc085ed2d79a/MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {url = "https://files.pythonhosted.org/packages/77/26/af46880038c6eac3832e751298f1965f3a550f38d1e9ddaabd674860076b/MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {url = "https://files.pythonhosted.org/packages/78/e6/91c9a20a943ea231c59024e181c4c5480097daf132428f2272670974637f/MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {url = "https://files.pythonhosted.org/packages/79/e2/b818bf277fa6b01244943498cb2127372c01dde5eff7682837cc72740618/MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {url = "https://files.pythonhosted.org/packages/7b/0f/0e99c2f342933c43af69849a6ba63f2eef54e14c6d0e10a26470fb6b40a9/MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {url = "https://files.pythonhosted.org/packages/7c/e6/454df09f18e0ea34d189b447a9e1a9f66c2aa332b77fd5577ebc7ca14d42/MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {url = "https://files.pythonhosted.org/packages/80/64/ccb65aadd71e7685caa69a885885a673e8748525a243fb26acea37201b44/MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {url = "https://files.pythonhosted.org/packages/82/70/b3978786c7b576c25d84b009d2a20a11b5300d252fc3ce984e37b932e97c/MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {url = "https://files.pythonhosted.org/packages/82/e3/4efcd74f10a7999783955aec36386f71082e6d7dafcc06b77b9df72b325a/MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {url = "https://files.pythonhosted.org/packages/87/a1/d0f05a09c6c1aef89d1eea0ab0ff1ea897d4117d27f1571034a7e3ff515b/MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {url = "https://files.pythonhosted.org/packages/93/ca/1c3ae0c6a5712d4ba98610cada03781ea0448436b17d1dcd4759115b15a1/MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {url = "https://files.pythonhosted.org/packages/93/fa/d72f68f84f8537ee8aa3e0764d1eb11e5e025a5ca90c16e94a40f894c2fc/MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {url = "https://files.pythonhosted.org/packages/95/7e/68018b70268fb4a2a605e2be44ab7b4dd7ce7808adae6c5ef32e34f4b55a/MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, + {url = "https://files.pythonhosted.org/packages/95/88/8c8cce021ac1b1eedde349c6a41f6c256da60babf95e572071361ff3f66b/MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {url = "https://files.pythonhosted.org/packages/96/92/a873b4a7fa20c2e30bffe883bb560330f3b6ce03aaf278f75f96d161935b/MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {url = "https://files.pythonhosted.org/packages/9d/80/8320f182d06a9b289b1a9f266f593feb91d3781c7e104bbe09e0c4c11439/MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {url = "https://files.pythonhosted.org/packages/be/18/988e1913a40cc8eb725b1e073eacc130f7803a061577bdc0b9343eb3c696/MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {url = "https://files.pythonhosted.org/packages/c3/e5/42842a44bfd9ba2955c562b1139334a2f64cedb687e8969777fd07de42a9/MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {url = "https://files.pythonhosted.org/packages/c7/0e/22d0c8e6ee84414e251bd1bc555b2705af6b3fb99f0ba1ead2a0f51d423b/MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {url = "https://files.pythonhosted.org/packages/cf/c1/d7596976a868fe3487212a382cc121358a53dc8e8d85ff2ee2c3d3b40f04/MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {url = "https://files.pythonhosted.org/packages/d1/10/ff89b23d4a24051c4e4f689b79ee06f230d7e9431445e24f5dd9d9a89730/MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {url = "https://files.pythonhosted.org/packages/e3/a9/e366665c7eae59c9c9d34b747cd5a3994847719a2304e0c8dec8b604dd98/MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {url = "https://files.pythonhosted.org/packages/e6/ff/d2378ca3cb3ac4a37af767b820b0f0bf3f5e9193a6acce0eefc379425c1c/MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {url = "https://files.pythonhosted.org/packages/e9/c6/2da36728c1310f141395176556500aeedfdea8c2b02a3b72ba61b69280e8/MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {url = "https://files.pythonhosted.org/packages/ea/60/2400ba59cf2465fa136487ee7299f52121a9d04b2cf8539ad43ad10e70e8/MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {url = "https://files.pythonhosted.org/packages/f9/aa/ebcd114deab08f892b1d70badda4436dbad1747f9e5b72cffb3de4c7129d/MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, +] +"mccabe 0.7.0" = [ + {url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] +"mypy-extensions 1.0.0" = [ + {url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] +"networkx 2.6.3" = [ + {url = "https://files.pythonhosted.org/packages/97/ae/7497bc5e1c84af95e585e3f98585c9f06c627fac6340984c4243053e8f44/networkx-2.6.3.tar.gz", hash = "sha256:c0946ed31d71f1b732b5aaa6da5a0388a345019af232ce2f49c766e2d6795c51"}, + {url = "https://files.pythonhosted.org/packages/e9/93/aa6613aa70d6eb4868e667068b5a11feca9645498fd31b954b6c4bb82fa5/networkx-2.6.3-py3-none-any.whl", hash = "sha256:80b6b89c77d1dfb64a4c7854981b60aeea6360ac02c6d4e4913319e0a313abef"}, +] +"packaging 23.0" = [ + {url = "https://files.pythonhosted.org/packages/47/d5/aca8ff6f49aa5565df1c826e7bf5e85a6df852ee063600c1efa5b932968c/packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, + {url = "https://files.pythonhosted.org/packages/ed/35/a31aed2993e398f6b09a790a181a7927eb14610ee8bbf02dc14d31677f1c/packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, +] +"pathspec 0.11.1" = [ + {url = "https://files.pythonhosted.org/packages/95/60/d93628975242cc515ab2b8f5b2fc831d8be2eff32f5a1be4776d49305d13/pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, + {url = "https://files.pythonhosted.org/packages/be/c8/551a803a6ebb174ec1c124e68b449b98a0961f0b737def601e3c1fbb4cfd/pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, +] +"platformdirs 3.1.1" = [ + {url = "https://files.pythonhosted.org/packages/79/c4/f98a05535344f79699bbd494e56ac9efc986b7a253fe9f4dba7414a7f505/platformdirs-3.1.1.tar.gz", hash = "sha256:024996549ee88ec1a9aa99ff7f8fc819bb59e2c3477b410d90a16d32d6e707aa"}, + {url = "https://files.pythonhosted.org/packages/7b/e1/593f693096c50411a2bf9571f66bc3be9d0f79a4a50e95aab581458b0e3c/platformdirs-3.1.1-py3-none-any.whl", hash = "sha256:e5986afb596e4bb5bde29a79ac9061aa955b94fca2399b7aaac4090860920dd8"}, +] +"pluggy 1.0.0" = [ + {url = "https://files.pythonhosted.org/packages/9e/01/f38e2ff29715251cf25532b9082a1589ab7e4f571ced434f98d0139336dc/pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {url = "https://files.pythonhosted.org/packages/a1/16/db2d7de3474b6e37cbb9c008965ee63835bba517e22cdb8c35b5116b5ce1/pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] "py4j 0.10.9.5" = [ {url = "https://files.pythonhosted.org/packages/86/ec/60880978512d5569ca4bf32b3b4d7776a528ecf4bca4523936c98c92a3c8/py4j-0.10.9.5-py2.py3-none-any.whl", hash = "sha256:52d171a6a2b031d8a5d1de6efe451cf4f5baff1a2819aabc3741c8406539ba04"}, {url = "https://files.pythonhosted.org/packages/ce/1f/b00295b6da3bd2f050912b9f71fdb436ae8f1601cf161365937d8553e24b/py4j-0.10.9.5.tar.gz", hash = "sha256:276a4a3c5a2154df1860ef3303a927460e02e97b047dc0a47c1c3fb8cce34db6"}, ] -"pyspark 3.2.3" = [ - {url = "https://files.pythonhosted.org/packages/de/ed/05d7fd2b3e8de9e590c4c0a15ff0c675001cb64f8ea98ffd5df1f80eaf3e/pyspark-3.2.3.tar.gz", hash = "sha256:eeefb33d8dcdd3ee60a5ed5b0028af06731992b4cc65dc6f06c85841ac8d6945"}, +"pycodestyle 2.9.1" = [ + {url = "https://files.pythonhosted.org/packages/67/e4/fc77f1039c34b3612c4867b69cbb2b8a4e569720b1f19b0637002ee03aff/pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, + {url = "https://files.pythonhosted.org/packages/b6/83/5bcaedba1f47200f0665ceb07bcb00e2be123192742ee0edfb66b600e5fd/pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, +] +"pyflakes 2.5.0" = [ + {url = "https://files.pythonhosted.org/packages/07/92/f0cb5381f752e89a598dd2850941e7f570ac3cb8ea4a344854de486db152/pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, + {url = "https://files.pythonhosted.org/packages/dc/13/63178f59f74e53acc2165aee4b002619a3cfa7eeaeac989a9eb41edf364e/pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, +] +"pyspark 3.3.2" = [ + {url = "https://files.pythonhosted.org/packages/6d/08/87b404b8b3255d46caf0ecdccf871d501a2b58da9b844d3f9710ce9d4d53/pyspark-3.3.2.tar.gz", hash = "sha256:0dfd5db4300c1f6cc9c16d8dbdfb82d881b4b172984da71344ede1a9d4893da8"}, +] +"pytest 7.2.2" = [ + {url = "https://files.pythonhosted.org/packages/b2/68/5321b5793bd506961bd40bdbdd0674e7de4fb873ee7cab33dd27283ad513/pytest-7.2.2-py3-none-any.whl", hash = "sha256:130328f552dcfac0b1cec75c12e3f005619dc5f874f0a06e8ff7263f0ee6225e"}, + {url = "https://files.pythonhosted.org/packages/b9/29/311895d9cd3f003dd58e8fdea36dd895ba2da5c0c90601836f7de79f76fe/pytest-7.2.2.tar.gz", hash = "sha256:c99ab0c73aceb050f68929bc93af19ab6db0558791c6a0715723abe9d0ade9d4"}, +] +"tomli 2.0.1" = [ + {url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +"typed-ast 1.5.4" = [ + {url = "https://files.pythonhosted.org/packages/04/93/482d12fd3334b53ec4087e658ab161ab23affcf8b052166b4cf972ca673b/typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {url = "https://files.pythonhosted.org/packages/07/d2/d55702e8deba2c80282fea0df53130790d8f398648be589750954c2dcce4/typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, + {url = "https://files.pythonhosted.org/packages/0b/e7/8ec06fc870254889198f933a595f139b7871b24bab1116d6128440731ea9/typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {url = "https://files.pythonhosted.org/packages/0f/59/430b86961d63278fcbced5ba72655ee93aa35e8e908bad4ff138480eb25d/typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {url = "https://files.pythonhosted.org/packages/1a/f6/dd891624aaf98b918d7012b9d01753d0192c4eb18cf33ce616c0e08f62ba/typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {url = "https://files.pythonhosted.org/packages/2f/87/25abe9558ed6cbd83ad5bfdccf7210a7eefaaf0232f86de99f65992e91fd/typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {url = "https://files.pythonhosted.org/packages/2f/d5/02059fe6ca70b11bb831007962323160372ca83843e0bf296e8b6d833198/typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {url = "https://files.pythonhosted.org/packages/34/2d/17fc1845dd5210345904b054c9fa90f451d64df56de0470f429bc8d63d39/typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {url = "https://files.pythonhosted.org/packages/38/54/48f7d5b1f954f3a4d8f76e1a11c8497ae899b900cd5a67f826fa3937f701/typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {url = "https://files.pythonhosted.org/packages/40/1a/5731a1a3908f60032aead10c2ffc9af12ee708bc9a156ed14a5065a9873a/typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {url = "https://files.pythonhosted.org/packages/48/6c/d96a545d337589dc5d7ecc0f8991122800ffec8dc10a24090619883b515e/typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {url = "https://files.pythonhosted.org/packages/4e/c1/cddc664ed3dd7d6bb62c80286c4e088b10556efc9a8db2049b425f8f23f7/typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {url = "https://files.pythonhosted.org/packages/5c/e3/f539e658614ebf5a521c8ba7cbbb98afc5f5e90ddb0332ea22c164612dad/typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {url = "https://files.pythonhosted.org/packages/70/2c/6d18e111d2c5422bb9e561bbf36885e430407859b2adef9b3fb575f189d5/typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {url = "https://files.pythonhosted.org/packages/78/18/3ecf5043f227ebd4a43af57e18e6a38f9fe0b81dbfbb8d62eec669d7b69e/typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {url = "https://files.pythonhosted.org/packages/96/35/612258bab9e1867b28e3137910df35576b7b0fbb9b6f3013cc23435a79ed/typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {url = "https://files.pythonhosted.org/packages/9b/d5/5540eb496c6817eaee8120fb759c7adb36f91ef647c6bb2877f09acc0569/typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {url = "https://files.pythonhosted.org/packages/c4/90/dacf9226b34961277f357c17c33b7cae3f05a5f5b8a1d23bd630d7a97a36/typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {url = "https://files.pythonhosted.org/packages/ca/da/fbc14befbf19d69d05b4b8b019edbc6554d958037a821c6d5585767fe0ff/typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {url = "https://files.pythonhosted.org/packages/cd/f3/188eede730be3f6ddb9a788cd6b7289207c5fceebbf8ae190f9716dd8c05/typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {url = "https://files.pythonhosted.org/packages/d8/4e/db9505b53c44d7bc324a3d2e09bdf82b0943d6e08b183ae382860f482a87/typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {url = "https://files.pythonhosted.org/packages/dd/87/09764c19a60a192b935579c93a07e781f6a52def10b723c8c5748e69a863/typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {url = "https://files.pythonhosted.org/packages/e3/7c/7407838e9c540031439f2948bce2763cdd6882ebb72cc0a25b763c10529e/typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {url = "https://files.pythonhosted.org/packages/f9/57/89ac0020d5ffc762487376d0c78e5d02af795657f18c411155b73de3c765/typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, ] -"typing-extensions 4.1.1" = [ - {url = "https://files.pythonhosted.org/packages/45/6b/44f7f8f1e110027cf88956b59f2fad776cca7e1704396d043f89effd3a0e/typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, - {url = "https://files.pythonhosted.org/packages/b1/5a/8b5fbb891ef3f81fc923bf3cb4a578c0abf9471eb50ce0f51c74212182ab/typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, +"typing-extensions 4.5.0" = [ + {url = "https://files.pythonhosted.org/packages/31/25/5abcd82372d3d4a3932e1fa8c3dbf9efac10cc7c0d16e78467460571b404/typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {url = "https://files.pythonhosted.org/packages/d3/20/06270dac7316220643c32ae61694e451c98f8caf4c8eab3aa80a2bedf0df/typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, ] -"werkzeug 2.0.3" = [ - {url = "https://files.pythonhosted.org/packages/6c/a8/60514fade2318e277453c9588545d0c335ea3ea6440ce5cdabfca7f73117/Werkzeug-2.0.3.tar.gz", hash = "sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c"}, - {url = "https://files.pythonhosted.org/packages/f4/f3/22afbdb20cc4654b10c98043414a14057cd27fdba9d4ae61cea596000ba2/Werkzeug-2.0.3-py3-none-any.whl", hash = "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8"}, +"werkzeug 2.2.3" = [ + {url = "https://files.pythonhosted.org/packages/02/3c/baaebf3235c87d61d6593467056d5a8fba7c75ac838b8d100a5e64eba7a0/Werkzeug-2.2.3.tar.gz", hash = "sha256:2e1ccc9417d4da358b9de6f174e3ac094391ea1d4fbef2d667865d819dfd0afe"}, + {url = "https://files.pythonhosted.org/packages/f6/f8/9da63c1617ae2a1dec2fbf6412f3a0cfe9d4ce029eccbda6e1e4258ca45f/Werkzeug-2.2.3-py3-none-any.whl", hash = "sha256:56433961bc1f12533306c624f3be5e744389ac61d722175d543e1751285da612"}, ] -"zipp 3.6.0" = [ - {url = "https://files.pythonhosted.org/packages/02/bf/0d03dbdedb83afec081fefe86cae3a2447250ef1a81ac601a9a56e785401/zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, - {url = "https://files.pythonhosted.org/packages/bd/df/d4a4974a3e3957fd1c1fa3082366d7fff6e428ddb55f074bf64876f8e8ad/zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, +"zipp 3.15.0" = [ + {url = "https://files.pythonhosted.org/packages/00/27/f0ac6b846684cecce1ee93d32450c45ab607f65c2e0255f0092032d91f07/zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, + {url = "https://files.pythonhosted.org/packages/5b/fa/c9e82bbe1af6266adf08afb563905eb87cab83fde00a0a08963510621047/zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, ] diff --git a/pyproject.toml b/pyproject.toml index d73acb7..f614e44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,9 +3,58 @@ [tool.pdm.scripts] ng_ai-api = {call = "ng_ai.ng_ai_api.__main__:run"} +[tool.pdm.scripts.test] +shell = """ + pdm update -dG dev + pytest +""" + +[tool.pdm.scripts.lint] +shell = """ + pdm update -dG lint + flake8 --max-line-length=84 ng_ai tests + isort --check ng_ai tests --diff + black --line-length 84 --check ng_ai tests --diff +""" + +[tool.pdm.scripts.format] +shell = """ + pdm update -dG lint + isort ng_ai tests + black --line-length 84 ng_ai tests +""" + +[tool.black] +line-length = 84 +exclude = ''' +/( + \.eggs + | \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist +)/ +''' + +[tool.isort] +profile = "black" +atomic = true +skip_glob = ["*/setup.py"] +filter_files = true + +[tool.pdm.dev-dependencies] +dev = [ + "pytest>=7.2.2", +] + [project] name = "ng_ai" -version = "0.2.9.1" +version = "0.2.9.2" description = "NebulaGraph Data Intelligence Suite" authors = [ {name = "Wey Gu", email = "weyl.gu@gmail.com"}, @@ -14,7 +63,8 @@ dependencies = [ "flask>=2.0.3", "networkx>=2.5.1", ] -requires-python = ">=3.6" +# pyspark for unit test cannot work with python 3.10 +requires-python = ">=3.7,<3.10" readme = "README.md" license = {text = "Apache-2.0"} @@ -25,4 +75,14 @@ build-backend = "pdm.pep517.api" [project.optional-dependencies] spark = ["pyspark>=3.2.3"] networkx = ["networkx>=2.5.1"] -all = ["ng_ai[spark,networkx]"] \ No newline at end of file +all = ["ng_ai[spark,networkx]"] +lint = [ + "flake8>=4.0.1", + "black>=23.1.0", + "isort>=5.11.5", +] +test = [ + "pytest>=7.2.2", + "chispa" +] +dev = ["ng_ai[all,lint,test]"] diff --git a/tests/test_nebula_algo.py b/tests/test_nebula_algo.py new file mode 100644 index 0000000..d2fa0c0 --- /dev/null +++ b/tests/test_nebula_algo.py @@ -0,0 +1,60 @@ +import sys +from unittest.mock import MagicMock + +import pytest +from pyspark.sql import SparkSession + +from ng_ai import NebulaGraphConfig +from ng_ai.engines import SparkEngine +from ng_ai.nebula_algo import NebulaDataFrameAlgorithm +from ng_ai.nebula_data import NebulaDataFrameObject + + +# Pytest fixture to create a SparkSession instance +@pytest.fixture(scope="session") +def spark(): + spark = ( + SparkSession.builder.master("local[1]") + .config("spark.port.maxRetries", "1000") + .config("spark.sql.shuffle.partitions", "1") + .getOrCreate() + ) + return spark + + +# Pytest fixture to create a sample Spark DataFrame +@pytest.fixture(scope="session") +def spark_df(spark): + """ + +---------+---------+-----+------+ + | _srcId| _dstId|_rank|degree| + +---------+---------+-----+------+ + |player105|player100| 0| 70| + |player105|player104| 0| 83| + +---------+---------+-----+------+ + """ + data = [ + ("player105", "player100", 0, 70), + ("player105", "player104", 0, 83), + ] + + # spark engine can only be tested in python 3.9 or lower + assert sys.version_info < ( + 3, + 10, + ), "spark engine can only be tested in python 3.9 or lower" + df = spark.createDataFrame(data, ["_srcId", "_dstId", "_rank", "degree"]) + return df + + +def test_algo_dot_call_spark_engine(spark_df): + config = NebulaGraphConfig() + spark_engine = SparkEngine(config=config) + nebula_df = NebulaDataFrameObject(engine=spark_engine, data=spark_df) + + mock_pagerank = MagicMock() + NebulaDataFrameAlgorithm.pagerank = mock_pagerank + + nebula_df.algo.pagerank(reset_prob=0.15, max_iter=10) + + mock_pagerank.assert_called_once_with(reset_prob=0.15, max_iter=10) diff --git a/tests/test_nebula_data.py b/tests/test_nebula_data.py new file mode 100644 index 0000000..4169902 --- /dev/null +++ b/tests/test_nebula_data.py @@ -0,0 +1,61 @@ +import sys + +import pytest +from chispa.dataframe_comparer import assert_df_equality +from pyspark.sql import SparkSession + +from ng_ai.config import NebulaGraphConfig +from ng_ai.engines import SparkEngine +from ng_ai.nebula_data import NebulaDataFrameObject, NebulaGraphObject + + +@pytest.fixture(scope="session") +def spark(): + spark = ( + SparkSession.builder.master("local[1]") + .config("spark.port.maxRetries", "1000") + .config("spark.sql.shuffle.partitions", "1") + .getOrCreate() + ) + return spark + + +@pytest.fixture(scope="session") +def spark_df(spark): + """ + +---------+---------+-----+------+ + | _srcId| _dstId|_rank|degree| + +---------+---------+-----+------+ + |player105|player100| 0| 70| + |player105|player104| 0| 83| + +---------+---------+-----+------+ + """ + data = [ + ("player105", "player100", 0, 70), + ("player105", "player104", 0, 83), + ] + + # spark engine can only be tested in python 3.9 or lower + assert sys.version_info < ( + 3, + 10, + ), "spark engine can only be tested in python 3.9 or lower" + df = spark.createDataFrame(data, ["_srcId", "_dstId", "_rank", "degree"]) + return df + + +def test_nebula_data_frame_object(spark_df): + config = NebulaGraphConfig() + spark_engine = SparkEngine(config=config) + nebula_df = NebulaDataFrameObject(engine=spark_engine, data=spark_df) + assert nebula_df.get_engine().type == "spark" + assert_df_equality(nebula_df.data, spark_df) + + +def test_nebula_graph_object(spark_df): + config = NebulaGraphConfig() + spark_engine = SparkEngine(config=config) + nebula_df = NebulaDataFrameObject(engine=spark_engine, data=spark_df) + nebula_graph = NebulaGraphObject(nebula_df) + assert nebula_graph.get_engine().type == "spark" + assert_df_equality(nebula_graph.df.data, spark_df) diff --git a/tests/test_nebula_reader.py b/tests/test_nebula_reader.py new file mode 100644 index 0000000..6249fc5 --- /dev/null +++ b/tests/test_nebula_reader.py @@ -0,0 +1,63 @@ +from unittest.mock import Mock + +import pytest + +from ng_ai.config import NebulaGraphConfig +from ng_ai.engines import SparkEngine +from ng_ai.nebula_data import NebulaDataFrameObject +from ng_ai.nebula_reader import NebulaReader, NebulaReaderWithSpark + + +@pytest.fixture +def nebula_reader_with_spark(): + config = NebulaGraphConfig() + return NebulaReaderWithSpark(config=config) + + +def test_nebula_reader_init(): + config = NebulaGraphConfig() + reader = NebulaReader(engine="spark", config=config) + + assert isinstance(reader, NebulaReader) + assert isinstance(reader.reader, NebulaReaderWithSpark) + + +def test_scan(nebula_reader_with_spark): + kwargs = {"edge": "follow", "props": "degree"} + + nebula_reader_with_spark.scan(**kwargs) + + assert nebula_reader_with_spark.raw_df_reader is not None + assert isinstance(nebula_reader_with_spark.engine, SparkEngine) + + spark = nebula_reader_with_spark.engine.spark + assert spark is not None + + +def test_query(nebula_reader_with_spark): + kwargs = { + "query": "MATCH ()-[e:follow]->() RETURN e LIMIT 100000", + "edge": "follow", + "props": "degree", + } + + nebula_reader_with_spark.query(**kwargs) + + assert nebula_reader_with_spark.raw_df_reader is not None + assert isinstance(nebula_reader_with_spark.engine, SparkEngine) + + spark = nebula_reader_with_spark.engine.spark + assert spark is not None + + +def test_read(nebula_reader_with_spark): + data = [[1, "foo"], [2, "bar"]] + mock_raw_df_reader = Mock() + mock_raw_df_reader.load.return_value = data + nebula_reader_with_spark.raw_df_reader = mock_raw_df_reader + + result = nebula_reader_with_spark.read() + + assert mock_raw_df_reader.load.called + assert isinstance(result, NebulaDataFrameObject) + assert nebula_reader_with_spark.raw_df == data diff --git a/tests/test_nebula_writer.py b/tests/test_nebula_writer.py new file mode 100644 index 0000000..21cac70 --- /dev/null +++ b/tests/test_nebula_writer.py @@ -0,0 +1,114 @@ +from unittest.mock import Mock, patch + +import pytest +from pyspark.sql import SparkSession + +from ng_ai.config import NebulaGraphConfig +from ng_ai.nebula_writer import NebulaWriter, NebulaWriterWithSpark + + +@pytest.fixture(scope="session") +def spark(): + spark = ( + SparkSession.builder.master("local[1]") + .config("spark.port.maxRetries", "1000") + .config("spark.sql.shuffle.partitions", "1") + .getOrCreate() + ) + return spark + + +@pytest.fixture(scope="session") +def spark_df(spark): + """ + +---------+---------+-----+------+ + | _srcId| _dstId|_rank|degree| + +---------+---------+-----+------+ + |player105|player100| 0| 70| + |player105|player104| 0| 83| + +---------+---------+-----+------+ + """ + data = [ + ("player105", "player100", 0, 70), + ("player105", "player104", 0, 83), + ] + df = spark.createDataFrame(data, ["_srcId", "_dstId", "_rank", "degree"]) + return df + + +# TBD, need to test writer with data: NebulaDataFrameObject +# @pytest.fixture +# def nebula_writer_with_spark_nebula_df(): +# config = NebulaGraphConfig() +# spark_engine = SparkEngine(config=config) +# data = NebulaDataFrameObject(engine=spark_engine, data=spark_df) +# return NebulaWriter(data, "nebulagraph_vertex", config=config, engine="spark") + + +@pytest.fixture +def nebula_writer_with_spark_jdf(spark_df): + config = NebulaGraphConfig() + data = spark_df._jdf + return NebulaWriter(data, "nebulagraph_vertex", config=config, engine="spark") + + +def test_nebula_writer_init(spark_df): + config = NebulaGraphConfig() + data = spark_df._jdf + writer = NebulaWriter(data, "nebulagraph_vertex", config=config, engine="spark") + + assert isinstance(writer, NebulaWriter) + assert isinstance(writer.writer, NebulaWriterWithSpark) + assert writer.jdf is not None + + +@patch("ng_ai.nebula_writer.NebulaWriterWithSpark._get_raw_df_writer_with_nebula") +def test_set_options_with_nebula( + mock_get_raw_df_writer, nebula_writer_with_spark_jdf +): + mock_raw_df_writer = Mock() + mock_get_raw_df_writer.return_value = mock_raw_df_writer + + properties = {"lpa": "cluster_id"} + + kwargs = { + "space": "basketballplayer", + "type": "vertex", + "tag": "person", + "write_mode": "insert", + "batch": 128, + "vid_policy": "", + "vid_field": "id", + "properties": properties, + } + nebula_writer_with_spark_jdf.set_options(**kwargs) + + assert mock_get_raw_df_writer.called + assert mock_raw_df_writer.option.called + assert mock_raw_df_writer.option.call_count == 11 + + +@patch("ng_ai.nebula_writer.NebulaWriterWithSpark._get_raw_df_writer_with_nebula") +def test_write(mock_get_raw_df_writer, nebula_writer_with_spark_jdf): + mock_raw_df_writer = Mock() + mock_get_raw_df_writer.return_value = mock_raw_df_writer + + # set up options for writing + properties = {"lpa": "cluster_id"} + + kwargs = { + "space": "basketballplayer", + "type": "vertex", + "tag": "person", + "write_mode": "insert", + "batch": 128, + "vid_policy": "", + "vid_field": "id", + "properties": properties, + } + + nebula_writer_with_spark_jdf.set_options(**kwargs) + + with patch.object(mock_raw_df_writer, "save") as mock_save: + nebula_writer_with_spark_jdf.write() + assert mock_save.called